//! Capability validation for ACP.
//!
//! This module implements validation logic to ensure that agent requests only
//! use capabilities that the client advertised as supported. Requests for
//! unsupported capabilities are rejected with clear error messages.

use crate::acp::connector_state::ClientCapabilities;
use crate::acp::transport::JsonRpcError;

/// JSON-RPC error code for method not found (unsupported capability).
pub const ERROR_METHOD_NOT_FOUND: i32 = -32601;

/// Result of capability validation.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CapabilityValidation {
    /// The capability is supported and the method can proceed.
    Supported,
    /// The capability is not supported.
    Unsupported(String),
}

/// Validate that a method is supported by the client's advertised capabilities.
///
/// This checks the method name against the client capabilities and returns
/// whether the method should be allowed to proceed.
///
/// # Arguments
///
/// * `method` - The JSON-RPC method name to validate
/// * `capabilities` - The client capabilities advertised during initialization
///
/// # Returns
///
/// `CapabilityValidation::Supported` if the method is allowed, or
/// `CapabilityValidation::Unsupported` with an error message if not.
pub fn validate_capability(
    method: &str,
    capabilities: &ClientCapabilities,
) -> CapabilityValidation {
    match method {
        // Filesystem capabilities
        "fs/read_text_file" => {
            if capabilities
                .fs
                .as_ref()
                .and_then(|fs| fs.read_text_file)
                .unwrap_or(false)
            {
                CapabilityValidation::Supported
            } else {
                CapabilityValidation::Unsupported(
                    "fs/read_text_file capability not advertised".to_string(),
                )
            }
        }
        "fs/write_text_file" => {
            if capabilities
                .fs
                .as_ref()
                .and_then(|fs| fs.write_text_file)
                .unwrap_or(false)
            {
                CapabilityValidation::Supported
            } else {
                CapabilityValidation::Unsupported(
                    "fs/write_text_file capability not advertised".to_string(),
                )
            }
        }

        // Terminal capabilities
        method if method.starts_with("terminal/") => {
            if capabilities.terminal.unwrap_or(false) {
                CapabilityValidation::Supported
            } else {
                CapabilityValidation::Unsupported(
                    "terminal capability not advertised".to_string(),
                )
            }
        }

        // All other methods are assumed to be core protocol methods that don't
        // require capability checks (initialize, authenticate, session/*, etc.)
        _ => CapabilityValidation::Supported,
    }
}

/// Create a JSON-RPC error for an unsupported capability.
///
/// This generates a properly formatted JSON-RPC error response that can be
/// sent back to the agent when it requests an unsupported method.
pub fn capability_not_supported_error(method: &str, reason: &str) -> JsonRpcError {
    JsonRpcError {
        code: ERROR_METHOD_NOT_FOUND,
        message: format!("Method not found: {} ({})", method, reason),
        data: None,
    }
}

/// Check if a method requires capability validation.
///
/// Core protocol methods (initialize, authenticate, session operations) do not
/// require capability checks. Tool methods (fs/*, terminal/*) do require checks.
pub fn requires_capability_check(method: &str) -> bool {
    method.starts_with("fs/") || method.starts_with("terminal/")
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::acp::connector_state::FsCapabilities;

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

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

    #[test]
    fn test_validate_read_capability_unsupported() {
        let caps = ClientCapabilities {
            fs: Some(FsCapabilities {
                read_text_file: Some(false),
                write_text_file: Some(false),
            }),
            terminal: Some(false),
            _meta: None,
        };

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

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

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

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

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

    #[test]
    fn test_validate_terminal_capability_supported() {
        let caps = ClientCapabilities {
            fs: None,
            terminal: Some(true),
            _meta: None,
        };

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

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

    #[test]
    fn test_validate_terminal_capability_unsupported() {
        let caps = ClientCapabilities {
            fs: None,
            terminal: Some(false),
            _meta: None,
        };

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

    #[test]
    fn test_validate_core_methods_always_supported() {
        let caps = ClientCapabilities {
            fs: None,
            terminal: Some(false),
            _meta: None,
        };

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

    #[test]
    fn test_validate_capability_missing_fs() {
        let caps = ClientCapabilities {
            fs: None,
            terminal: Some(false),
            _meta: None,
        };

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

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

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

    #[test]
    fn test_requires_capability_check() {
        // Tool methods require checks
        assert!(requires_capability_check("fs/read_text_file"));
        assert!(requires_capability_check("fs/write_text_file"));
        assert!(requires_capability_check("terminal/execute"));

        // Core methods don't require checks
        assert!(!requires_capability_check("initialize"));
        assert!(!requires_capability_check("authenticate"));
        assert!(!requires_capability_check("session/new"));
        assert!(!requires_capability_check("session/prompt"));
    }
}
