//! Integration tests for ACP initialization and capability negotiation.
//!
//! These tests verify that the ACP protocol implementation correctly performs:
//! - Protocol version negotiation
//! - Capability exchange
//! - Optional authentication
//! - Capability validation
//!
//! Tests use the dirigate for both stdio and HTTP transport modes.

#[cfg(feature = "server")]
mod initialization_tests {
    use dirigent_core::acp::{
        authenticate, capabilities, connector_state::*, initialize,
    };

    /// Test helper to create default client info.
    fn client_info() -> ImplementationInfo {
        ImplementationInfo {
            name: "dirigent-test".to_string(),
            title: Some("Dirigent Test Client".to_string()),
            version: Some("0.1.0".to_string()),
        }
    }

    #[tokio::test]
    async fn test_initialize_request_serialization() {
        let caps = ClientCapabilities::default_safe();
        let request = initialize::InitializeRequest::new(caps, Some(client_info()));

        // Verify serialization
        let jsonrpc = request.to_jsonrpc(1);
        assert_eq!(jsonrpc.method, "initialize");
        assert_eq!(jsonrpc.jsonrpc, "2.0");
        assert!(jsonrpc.params.is_some());

        // Verify the serialized params contain expected fields
        let params = jsonrpc.params.unwrap();
        assert!(params.get("protocol_version").is_some());
        assert!(params.get("client_capabilities").is_some());
        assert!(params.get("client_info").is_some());
    }

    #[tokio::test]
    async fn test_initialize_response_parsing() {
        let json = serde_json::json!({
            "protocol_version": 1,
            "agent_capabilities": {
                "load_session": true,
                "prompt_capabilities": {
                    "image": true,
                    "audio": false,
                    "embedded_context": true
                }
            },
            "agent_info": {
                "name": "test-agent",
                "version": "1.0.0"
            },
            "auth_methods": []
        });

        let response = initialize::InitializeResponse::from_jsonrpc(&json).unwrap();

        assert_eq!(response.protocol_version, 1);
        assert!(response.agent_capabilities.supports_load_session());
        assert!(response.agent_capabilities.supports_image());
        assert!(!response.agent_capabilities.supports_audio());
        assert!(response.agent_capabilities.supports_embedded_context());
        assert_eq!(response.auth_methods.len(), 0);
    }

    #[tokio::test]
    async fn test_version_mismatch_detection() {
        let client_version = 1;
        let agent_version = 2;

        assert!(!initialize::is_version_compatible(
            client_version,
            agent_version
        ));
        assert!(initialize::is_version_compatible(client_version, 1));
    }

    #[tokio::test]
    async fn test_authenticate_request_redacts_credentials() {
        let creds = serde_json::json!({"api_key": "super_secret_key_12345"});
        let request = authenticate::AuthenticateRequest::new("api_key", creds);

        let debug_str = format!("{:?}", request);

        // Should NOT contain the actual secret
        assert!(!debug_str.contains("super_secret_key"));
        assert!(!debug_str.contains("12345"));

        // Should show that it's redacted
        assert!(debug_str.contains("REDACTED"));

        // Should still show the method
        assert!(debug_str.contains("api_key"));
    }

    #[tokio::test]
    async fn test_authenticate_response_parsing() {
        let success_json = serde_json::json!({
            "success": true
        });

        let response = authenticate::AuthenticateResponse::from_jsonrpc(&success_json).unwrap();
        assert!(response.success);
        assert!(response.error.is_none());

        let failure_json = serde_json::json!({
            "success": false,
            "error": "Invalid credentials"
        });

        let response = authenticate::AuthenticateResponse::from_jsonrpc(&failure_json).unwrap();
        assert!(!response.success);
        assert_eq!(response.error, Some("Invalid credentials".to_string()));
    }

    #[tokio::test]
    async fn test_auth_required_check() {
        assert!(!authenticate::is_auth_required(&[]));
        assert!(authenticate::is_auth_required(&["api_key".to_string()]));
        assert!(authenticate::is_auth_required(&[
            "api_key".to_string(),
            "oauth".to_string()
        ]));
    }

    #[tokio::test]
    async fn test_capability_validation_fs_read() {
        let caps_enabled = ClientCapabilities {
            fs: Some(FsCapabilities {
                read_text_file: Some(true),
                write_text_file: Some(false),
            }),
            terminal: Some(false),
            _meta: None,
        };

        let result = capabilities::validate_capability("fs/read_text_file", &caps_enabled);
        assert_eq!(result, capabilities::CapabilityValidation::Supported);

        let caps_disabled = ClientCapabilities {
            fs: Some(FsCapabilities {
                read_text_file: Some(false),
                write_text_file: Some(false),
            }),
            terminal: Some(false),
            _meta: None,
        };

        let result = capabilities::validate_capability("fs/read_text_file", &caps_disabled);
        assert!(matches!(
            result,
            capabilities::CapabilityValidation::Unsupported(_)
        ));
    }

    #[tokio::test]
    async fn test_capability_validation_fs_write() {
        let caps_enabled = ClientCapabilities {
            fs: Some(FsCapabilities {
                read_text_file: Some(true),
                write_text_file: Some(true),
            }),
            terminal: Some(false),
            _meta: None,
        };

        let result = capabilities::validate_capability("fs/write_text_file", &caps_enabled);
        assert_eq!(result, capabilities::CapabilityValidation::Supported);

        let caps_disabled = ClientCapabilities {
            fs: Some(FsCapabilities {
                read_text_file: Some(true),
                write_text_file: Some(false),
            }),
            terminal: Some(false),
            _meta: None,
        };

        let result = capabilities::validate_capability("fs/write_text_file", &caps_disabled);
        assert!(matches!(
            result,
            capabilities::CapabilityValidation::Unsupported(_)
        ));
    }

    #[tokio::test]
    async fn test_capability_validation_terminal() {
        let caps_enabled = ClientCapabilities {
            fs: None,
            terminal: Some(true),
            _meta: None,
        };

        let result = capabilities::validate_capability("terminal/execute", &caps_enabled);
        assert_eq!(result, capabilities::CapabilityValidation::Supported);

        let result = capabilities::validate_capability("terminal/read", &caps_enabled);
        assert_eq!(result, capabilities::CapabilityValidation::Supported);

        let caps_disabled = ClientCapabilities {
            fs: None,
            terminal: Some(false),
            _meta: None,
        };

        let result = capabilities::validate_capability("terminal/execute", &caps_disabled);
        assert!(matches!(
            result,
            capabilities::CapabilityValidation::Unsupported(_)
        ));
    }

    #[tokio::test]
    async fn test_capability_validation_core_methods() {
        let minimal_caps = ClientCapabilities {
            fs: None,
            terminal: Some(false),
            _meta: None,
        };

        // Core protocol methods should always be supported
        assert_eq!(
            capabilities::validate_capability("initialize", &minimal_caps),
            capabilities::CapabilityValidation::Supported
        );
        assert_eq!(
            capabilities::validate_capability("authenticate", &minimal_caps),
            capabilities::CapabilityValidation::Supported
        );
        assert_eq!(
            capabilities::validate_capability("session/new", &minimal_caps),
            capabilities::CapabilityValidation::Supported
        );
        assert_eq!(
            capabilities::validate_capability("session/prompt", &minimal_caps),
            capabilities::CapabilityValidation::Supported
        );
    }

    #[tokio::test]
    async fn test_requires_capability_check() {
        assert!(capabilities::requires_capability_check("fs/read_text_file"));
        assert!(capabilities::requires_capability_check("fs/write_text_file"));
        assert!(capabilities::requires_capability_check("terminal/execute"));

        assert!(!capabilities::requires_capability_check("initialize"));
        assert!(!capabilities::requires_capability_check("authenticate"));
        assert!(!capabilities::requires_capability_check("session/new"));
    }

    #[tokio::test]
    async fn test_connector_state_lifecycle() {
        let mut state = AcpConnectorState::new();

        assert_eq!(state.connection_state, ConnectionState::Uninitialized);
        assert!(!state.is_ready());

        state.connection_state = ConnectionState::Initializing;
        assert!(!state.is_ready());

        state.connection_state = ConnectionState::Initialized;
        assert!(!state.is_ready());

        state.connection_state = ConnectionState::Ready;
        assert!(state.is_ready());

        state.connection_state = ConnectionState::Disconnected;
        assert!(!state.is_ready());
    }

    #[tokio::test]
    async fn test_connector_state_auth_required() {
        let mut state = AcpConnectorState::new();

        assert!(!state.requires_auth());

        state.auth_methods = vec!["api_key".to_string()];
        assert!(state.requires_auth());

        state.authenticated = true;
        assert!(!state.requires_auth());
    }

    #[tokio::test]
    async fn test_connector_state_error() {
        let mut state = AcpConnectorState::new();

        state.set_error("Test error");

        match state.connection_state {
            ConnectionState::Error(msg) => {
                assert_eq!(msg, "Test error");
            }
            _ => panic!("Expected Error state"),
        }
    }

    #[tokio::test]
    async fn test_capability_not_supported_error() {
        let error = capabilities::capability_not_supported_error(
            "fs/write_text_file",
            "write capability not enabled",
        );

        assert_eq!(error.code, capabilities::ERROR_METHOD_NOT_FOUND);
        assert!(error.message.contains("fs/write_text_file"));
        assert!(error.message.contains("write capability not enabled"));
    }

    #[tokio::test]
    async fn test_agent_capabilities_helpers() {
        let caps = AgentCapabilities {
            load_session: Some(true),
            prompt_capabilities: Some(PromptCapabilities {
                image: Some(true),
                audio: Some(false),
                embedded_context: Some(true),
            }),
            mcp: Some(McpCapabilities {
                http: Some(true),
                sse: Some(false),
            }),
            _meta: None,
        };

        assert!(caps.supports_load_session());
        assert!(caps.supports_image());
        assert!(!caps.supports_audio());
        assert!(caps.supports_embedded_context());
    }

    #[tokio::test]
    async fn test_client_capabilities_presets() {
        let safe_caps = ClientCapabilities::default_safe();
        assert_eq!(
            safe_caps.fs.as_ref().unwrap().read_text_file,
            Some(true)
        );
        assert_eq!(
            safe_caps.fs.as_ref().unwrap().write_text_file,
            Some(false)
        );
        assert_eq!(safe_caps.terminal, Some(false));

        let all_caps = ClientCapabilities::all_enabled();
        assert_eq!(all_caps.fs.as_ref().unwrap().read_text_file, Some(true));
        assert_eq!(all_caps.fs.as_ref().unwrap().write_text_file, Some(true));
        assert_eq!(all_caps.terminal, Some(true));
    }
}
