//! Serde-compatible wire types for Sourse.
//!
//! This crate defines the protocol structs for clone, fetch, and push
//! operations. It has no server framework dependency; serialization
//! compatibility is validated through round-trip tests.

use sourse_core::ObjectId;

/// The protocol version for wire compatibility.
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ProtocolVersion {
    /// Major version.
    pub major: u32,
    /// Minor version.
    pub minor: u32,
}

impl ProtocolVersion {
    /// The current protocol version.
    pub fn current() -> Self {
        Self { major: 0, minor: 1 }
    }
}

/// A clone request from client to server.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CloneRequest {
    /// Protocol version.
    pub version: ProtocolVersion,
    /// Repository identifier to clone.
    pub repository: String,
}

/// A clone response from server to client.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CloneResponse {
    /// Protocol version.
    pub version: ProtocolVersion,
    /// Root manifest object ID.
    pub manifest_root: ObjectId,
    /// Head commit object ID.
    pub head_commit: ObjectId,
}

/// A fetch request from client to server.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct FetchRequest {
    /// Protocol version.
    pub version: ProtocolVersion,
    /// Object IDs the client already has.
    pub have: Vec<ObjectId>,
    /// Object IDs the client wants.
    pub want: Vec<ObjectId>,
}

/// A fetch response from server to client.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct FetchResponse {
    /// Protocol version.
    pub version: ProtocolVersion,
    /// Object data being sent.
    pub objects: Vec<ObjectId>,
}

/// A push request from client to server.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PushRequest {
    /// Protocol version.
    pub version: ProtocolVersion,
    /// Object IDs being pushed.
    pub objects: Vec<ObjectId>,
}

/// A push response from server to client.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PushResponse {
    /// Protocol version.
    pub version: ProtocolVersion,
    /// Whether the push was accepted.
    pub accepted: bool,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn protocol_version_round_trip() {
        let v = ProtocolVersion::current();
        let json = serde_json::to_string(&v).unwrap();
        let v2: ProtocolVersion = serde_json::from_str(&json).unwrap();
        assert_eq!(v, v2);
    }

    #[test]
    fn clone_request_round_trip() {
        let req = CloneRequest {
            version: ProtocolVersion::current(),
            repository: "test-repo".to_string(),
        };
        let json = serde_json::to_string(&req).unwrap();
        let req2: CloneRequest = serde_json::from_str(&json).unwrap();
        assert_eq!(req.repository, req2.repository);
    }

    #[test]
    fn fetch_request_round_trip() {
        let req = FetchRequest {
            version: ProtocolVersion::current(),
            have: vec![ObjectId("aaa".to_string())],
            want: vec![ObjectId("bbb".to_string())],
        };
        let json = serde_json::to_string(&req).unwrap();
        let req2: FetchRequest = serde_json::from_str(&json).unwrap();
        assert_eq!(req.have.len(), req2.have.len());
        assert_eq!(req.want.len(), req2.want.len());
    }

    #[test]
    fn push_request_round_trip() {
        let req = PushRequest {
            version: ProtocolVersion::current(),
            objects: vec![ObjectId("ccc".to_string())],
        };
        let json = serde_json::to_string(&req).unwrap();
        let req2: PushRequest = serde_json::from_str(&json).unwrap();
        assert_eq!(req.objects.len(), req2.objects.len());
    }

    #[test]
    fn fetch_response_round_trip() {
        let resp = FetchResponse {
            version: ProtocolVersion::current(),
            objects: vec![ObjectId("obj1".to_string()), ObjectId("obj2".to_string())],
        };
        let json = serde_json::to_string(&resp).unwrap();
        let resp2: FetchResponse = serde_json::from_str(&json).unwrap();
        assert_eq!(resp.objects.len(), resp2.objects.len());
    }

    #[test]
    fn push_response_round_trip() {
        let resp = PushResponse {
            version: ProtocolVersion::current(),
            accepted: true,
        };
        let json = serde_json::to_string(&resp).unwrap();
        let resp2: PushResponse = serde_json::from_str(&json).unwrap();
        assert!(resp2.accepted);
    }

    #[test]
    fn no_server_framework_dependency() {
        // This test exists to document that sourse-protocol must not depend
        // on axum, tower, or any server framework.
        // The manifest should be checked manually for this invariant.
    }
}
