//! Channel domain logic — 1:1 DM channel creation.

use std::sync::Arc;

use crate::storage::Store;

use super::types::*;

/// Domain service for 1:1 channel management.
pub struct ChannelService {
    pub store: Arc<dyn Store>,
}

impl ChannelService {
    /// Create or look up a 1:1 DM channel between caller and peer.
    ///
    /// The channel ID is deterministic and symmetric — `create(A, B)` returns
    /// the same ID as `create(B, A)`. Returns `was_new = true` on first creation.
    pub fn create_channel(
        &self,
        req: CreateChannelReq,
        caller_identity_key: &[u8],
    ) -> Result<CreateChannelResp, DomainError> {
        if req.peer_key.len() != 32 {
            return Err(DomainError::InvalidIdentityKey(req.peer_key.len()));
        }
        if caller_identity_key == req.peer_key.as_slice() {
            return Err(DomainError::BadParams(
                "peer_key must not equal caller identity".into(),
            ));
        }

        let (channel_id, was_new) = self
            .store
            .create_channel(caller_identity_key, &req.peer_key)?;

        Ok(CreateChannelResp {
            channel_id,
            was_new,
        })
    }
}

#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
    use super::*;
    use crate::storage::FileBackedStore;

    fn test_service() -> (tempfile::TempDir, ChannelService) {
        let dir = tempfile::tempdir().unwrap();
        let store = Arc::new(FileBackedStore::open(dir.path()).unwrap());
        let svc = ChannelService { store };
        (dir, svc)
    }

    #[test]
    fn create_channel_success() {
        let (_dir, svc) = test_service();
        let caller = vec![1u8; 32];
        let peer = vec![2u8; 32];

        let resp = svc
            .create_channel(CreateChannelReq { peer_key: peer.clone() }, &caller)
            .unwrap();
        assert!(resp.was_new);
        assert_eq!(resp.channel_id.len(), 16);
    }

    #[test]
    fn create_channel_idempotent() {
        let (_dir, svc) = test_service();
        let caller = vec![1u8; 32];
        let peer = vec![2u8; 32];

        let resp1 = svc
            .create_channel(CreateChannelReq { peer_key: peer.clone() }, &caller)
            .unwrap();
        assert!(resp1.was_new);

        let resp2 = svc
            .create_channel(CreateChannelReq { peer_key: peer.clone() }, &caller)
            .unwrap();
        assert!(!resp2.was_new);
        assert_eq!(resp1.channel_id, resp2.channel_id);
    }

    #[test]
    fn create_channel_symmetric() {
        let (_dir, svc) = test_service();
        let a = vec![1u8; 32];
        let b = vec![2u8; 32];

        let resp_ab = svc
            .create_channel(CreateChannelReq { peer_key: b.clone() }, &a)
            .unwrap();
        let resp_ba = svc
            .create_channel(CreateChannelReq { peer_key: a.clone() }, &b)
            .unwrap();
        // Same channel regardless of who initiates
        assert_eq!(resp_ab.channel_id, resp_ba.channel_id);
    }

    #[test]
    fn create_channel_rejects_invalid_peer_key_length() {
        let (_dir, svc) = test_service();
        let caller = vec![1u8; 32];

        let err = svc
            .create_channel(CreateChannelReq { peer_key: vec![1u8; 31] }, &caller)
            .unwrap_err();
        assert!(matches!(err, DomainError::InvalidIdentityKey(31)));

        let err = svc
            .create_channel(CreateChannelReq { peer_key: vec![1u8; 33] }, &caller)
            .unwrap_err();
        assert!(matches!(err, DomainError::InvalidIdentityKey(33)));

        let err = svc
            .create_channel(CreateChannelReq { peer_key: vec![] }, &caller)
            .unwrap_err();
        assert!(matches!(err, DomainError::InvalidIdentityKey(0)));
    }

    #[test]
    fn create_channel_rejects_self_channel() {
        let (_dir, svc) = test_service();
        let me = vec![5u8; 32];

        let err = svc
            .create_channel(CreateChannelReq { peer_key: me.clone() }, &me)
            .unwrap_err();
        assert!(matches!(err, DomainError::BadParams(_)));
    }
}
