//! Glue between the statement subscription system and ChatManager.
//!
//! `ChatDispatchHandler` implements `StatementHandler` (from host-chain-core)
//! by forwarding each raw statement to `ChatManager::handle_incoming_statement`.
//!
//! Gated behind the `chain-subscription` feature to avoid adding host-chain-core
//! as a mandatory dependency.

use std::sync::Arc;

use host_chain_core::subscription::StatementHandler;
use host_encoding::statement_store::Statement;

use crate::traits::{ChatEventSink, ChatKeyResolver, ChatTransport};
use crate::ChatManager;

/// Wraps a `ChatManager` to satisfy the `StatementHandler` trait.
///
/// The signing key is owned by the `ChatManager` via its stored `HostSigner`,
/// so no signing closure needs to be captured here.
pub struct ChatDispatchHandler<T: ChatTransport, K: ChatKeyResolver, E: ChatEventSink> {
    manager: Arc<ChatManager<T, K, E>>,
}

impl<T, K, E> ChatDispatchHandler<T, K, E>
where
    T: ChatTransport + 'static,
    K: ChatKeyResolver + 'static,
    E: ChatEventSink + 'static,
{
    /// Construct a handler that forwards statements to the given `ChatManager`.
    pub fn new(manager: Arc<ChatManager<T, K, E>>) -> Self {
        Self { manager }
    }
}

impl<T, K, E> StatementHandler for ChatDispatchHandler<T, K, E>
where
    T: ChatTransport + 'static,
    K: ChatKeyResolver + 'static,
    E: ChatEventSink + 'static,
{
    fn on_statement(&self, _statement: &Statement, raw: &[u8]) -> Result<(), String> {
        self.manager
            .handle_incoming_statement(raw)
            .map_err(|e| e.to_string())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::events::{ChatMessageEvent, ChatRequestEvent, DeliveryStatusEvent};
    use crate::manager::tests::DummySigner;
    use crate::manager::{build_message_payload, tests};
    use crate::traits::{ChatEventSink, ChatKeyResolver, ChatTransport, StatementCallback};
    use crate::{ChatManager, ContentType};
    use std::sync::{Arc, Mutex};

    // -----------------------------------------------------------------------
    // Minimal stubs (mirror of manager.rs test stubs — kept local)
    // -----------------------------------------------------------------------

    struct NoopTransport;
    impl ChatTransport for NoopTransport {
        fn submit_statement(&self, _encoded: &[u8]) -> Result<(), String> {
            Ok(())
        }
        fn subscribe(
            &self,
            _topics: &[[u8; 32]],
            _callback: StatementCallback,
        ) -> Result<(), String> {
            Ok(())
        }
        fn unsubscribe(&self, _topics: &[[u8; 32]]) -> Result<(), String> {
            Ok(())
        }
    }

    struct FixedKeyResolver {
        pubkey: [u8; 65],
    }
    impl ChatKeyResolver for FixedKeyResolver {
        fn resolve_p256_pubkey(&self, _account_id: &[u8; 32]) -> Result<[u8; 65], String> {
            Ok(self.pubkey)
        }
    }

    #[derive(Default)]
    struct RecordingSink {
        messages: Arc<Mutex<Vec<ChatMessageEvent>>>,
    }
    impl ChatEventSink for RecordingSink {
        fn push_message(&self, event: ChatMessageEvent) {
            self.messages
                .lock()
                .unwrap_or_else(|e| e.into_inner())
                .push(event);
        }
        fn push_delivery_status(&self, _event: DeliveryStatusEvent) {}
        fn push_chat_request(&self, _event: ChatRequestEvent) {}
    }

    // -----------------------------------------------------------------------
    // Tests
    // -----------------------------------------------------------------------

    /// `ChatDispatchHandler::on_statement` must forward a valid signed+encrypted
    /// statement to the `ChatManager` and produce a `ChatMessageEvent` on the sink.
    #[test]
    fn test_dispatch_handler_forwards_to_chat_manager() {
        let (peer_kp, peer_id) = tests::test_sr25519_keypair(0x02);
        let (local_secret, local_pub, local_id) = tests::test_p256_keypair(0x01);

        // Derive a fixed P-256 pubkey for the peer (ECDH session setup only).
        let peer_p256: [u8; 65] = {
            use p256::elliptic_curve::sec1::ToEncodedPoint;
            let mut s = [0x02u8; 32];
            s[0] = 0x01;
            let sk = p256::SecretKey::from_slice(&s).unwrap();
            sk.public_key()
                .to_encoded_point(false)
                .as_bytes()
                .try_into()
                .unwrap()
        };

        let sink = RecordingSink::default();
        let messages = Arc::clone(&sink.messages);

        let manager = Arc::new(ChatManager::new(
            local_id,
            local_secret,
            local_pub,
            Arc::new(DummySigner),
            NoopTransport,
            FixedKeyResolver { pubkey: peer_p256 },
            sink,
        ));
        manager.open_conversation(peer_id).unwrap();

        // Retrieve session crypto via the test helper — no private field access.
        let (shared_secret, peer_request_channel) = manager
            .test_session_crypto(&peer_id)
            .expect("session must exist after open_conversation");

        let plaintext =
            build_message_payload(ContentType::Text, "dispatch-test-id", b"dispatched!");
        let ciphertext = crate::crypto::encrypt(&shared_secret, &plaintext);
        let raw =
            tests::build_signed_statement(&peer_kp, &peer_id, &peer_request_channel, &ciphertext);

        // Wrap in ChatDispatchHandler and call on_statement.
        let handler = ChatDispatchHandler::new(Arc::clone(&manager));

        let decoded = host_encoding::statement_store::decode_statement(&raw).unwrap();
        handler.on_statement(&decoded, &raw).unwrap();

        let evts = messages.lock().unwrap_or_else(|e| e.into_inner());
        assert_eq!(evts.len(), 1, "event sink must receive exactly one message");
        assert_eq!(evts[0].text.as_deref(), Some("dispatched!"));
        assert_eq!(evts[0].peer_account_id, peer_id);
    }

    /// `on_statement` must return `Ok(())` for a statement on an unknown channel;
    /// the manager silently drops those and the handler must propagate `Ok`.
    #[test]
    fn test_dispatch_handler_returns_ok_for_unknown_channel() {
        let (local_secret, local_pub, local_id) = tests::test_p256_keypair(0x01);

        let manager = Arc::new(ChatManager::new(
            local_id,
            local_secret,
            local_pub,
            Arc::new(DummySigner),
            NoopTransport,
            FixedKeyResolver { pubkey: [0u8; 65] },
            RecordingSink::default(),
        ));

        let handler = ChatDispatchHandler::new(manager);

        // Statement addressed to a random channel not registered in any session.
        let unknown_channel = [0xabu8; 32];
        let (peer_kp, peer_id) = tests::test_sr25519_keypair(0x05);
        let raw =
            tests::build_signed_statement(&peer_kp, &peer_id, &unknown_channel, b"irrelevant data");
        let decoded = host_encoding::statement_store::decode_statement(&raw).unwrap();

        // Must not panic and must return Ok (manager silently drops unknown channels).
        assert!(handler.on_statement(&decoded, &raw).is_ok());
    }

    /// Providing garbage as the `raw` bytes must yield `Err` (decode failure
    /// inside `handle_incoming_statement`).
    #[test]
    fn test_dispatch_handler_returns_err_for_malformed_bytes() {
        let (local_secret, local_pub, local_id) = tests::test_p256_keypair(0x01);

        let manager = Arc::new(ChatManager::new(
            local_id,
            local_secret,
            local_pub,
            Arc::new(DummySigner),
            NoopTransport,
            FixedKeyResolver { pubkey: [0u8; 65] },
            RecordingSink::default(),
        ));

        let handler = ChatDispatchHandler::new(manager);

        // Build a valid statement to decode for the trait parameter, but pass
        // garbage as the raw bytes — the manager's decode step must fail.
        let (peer_kp, peer_id) = tests::test_sr25519_keypair(0x06);
        let dummy_channel = [0u8; 32];
        let valid_raw = tests::build_signed_statement(&peer_kp, &peer_id, &dummy_channel, b"x");
        let decoded = host_encoding::statement_store::decode_statement(&valid_raw).unwrap();

        let result = handler.on_statement(&decoded, &[0xff, 0x00, 0xde, 0xad]);
        assert!(
            result.is_err(),
            "malformed raw bytes must produce an Err from on_statement"
        );
    }
}
