//! Bridge between typedialog FieldDefinition and encrypt crate BackendSpec.
//!
//! Converts field encryption configuration to BackendSpec for use with the
//! unified encryption API from the `encrypt` crate.

use crate::error::Result;
use crate::form_parser::FieldDefinition;
use std::collections::HashMap;

/// Convert FieldDefinition encryption configuration to BackendSpec.
///
/// Extracts backend name and configuration from a field definition and
/// converts it to a BackendSpec suitable for use with the `encrypt` crate.
///
/// # Arguments
///
/// * `field` - Field definition containing encryption backend and config
/// * `default_backend` - Default backend name if field doesn't specify one
///
/// # Returns
///
/// BackendSpec configured according to field settings, or error if config incomplete
///
/// # Examples
///
/// ```ignore
/// use typedialog_core::encryption_bridge;
/// use typedialog_core::form_parser::FieldDefinition;
///
/// let field = FieldDefinition {
///     encryption_backend: Some("age".to_string()),
///     ..Default::default()
/// };
///
/// let spec = encryption_bridge::field_to_backend_spec(&field, None)?;
/// assert_eq!(spec.backend_name(), "age");
/// # Ok::<(), typedialog_core::error::Error>(())
/// ```
pub fn field_to_backend_spec(
    field: &FieldDefinition,
    default_backend: Option<&str>,
) -> Result<encrypt::BackendSpec> {
    let backend_name = field
        .encryption_backend
        .as_deref()
        .or(default_backend)
        .unwrap_or("age");

    let empty_config = HashMap::new();
    let config = field.encryption_config.as_ref().unwrap_or(&empty_config);

    match backend_name {
        "age" => {
            // Key file path from config, or default
            if let Some(key_file) = config.get("key_file") {
                Ok(encrypt::BackendSpec::age(key_file.clone()))
            } else {
                Ok(encrypt::BackendSpec::age_default())
            }
        }
        "sops" => Ok(encrypt::BackendSpec::sops()),
        "secretumvault" => {
            let vault_addr = config
                .get("vault_addr")
                .or_else(|| config.get("address"))
                .ok_or_else(|| {
                    crate::error::ErrorWrapper::new(
                        "SecretumVault backend requires vault_addr in encryption_config"
                            .to_string(),
                    )
                })?;

            let vault_token = config
                .get("vault_token")
                .or_else(|| config.get("token"))
                .ok_or_else(|| {
                    crate::error::ErrorWrapper::new(
                        "SecretumVault backend requires vault_token in encryption_config"
                            .to_string(),
                    )
                })?;

            let key_name = config
                .get("key_name")
                .or_else(|| config.get("key"))
                .cloned()
                .unwrap_or_else(|| "default".to_string());

            Ok(encrypt::BackendSpec::secretumvault(
                vault_addr.clone(),
                vault_token.clone(),
                key_name,
            ))
        }
        "awskms" => {
            let region = config.get("region").ok_or_else(|| {
                crate::error::ErrorWrapper::new(
                    "AWS KMS backend requires region in encryption_config".to_string(),
                )
            })?;

            let key_id = config
                .get("key_id")
                .or_else(|| config.get("key_arn"))
                .ok_or_else(|| {
                    crate::error::ErrorWrapper::new(
                        "AWS KMS backend requires key_id or key_arn in encryption_config"
                            .to_string(),
                    )
                })?;

            Ok(encrypt::BackendSpec::aws_kms(
                region.clone(),
                key_id.clone(),
            ))
        }
        "gcpkms" => {
            let project_id = config.get("project_id").ok_or_else(|| {
                crate::error::ErrorWrapper::new(
                    "GCP KMS backend requires project_id in encryption_config".to_string(),
                )
            })?;

            let key_ring = config.get("key_ring").ok_or_else(|| {
                crate::error::ErrorWrapper::new(
                    "GCP KMS backend requires key_ring in encryption_config".to_string(),
                )
            })?;

            let crypto_key = config
                .get("crypto_key")
                .or_else(|| config.get("key"))
                .ok_or_else(|| {
                    crate::error::ErrorWrapper::new(
                        "GCP KMS backend requires crypto_key in encryption_config".to_string(),
                    )
                })?;

            let location = config
                .get("location")
                .cloned()
                .unwrap_or_else(|| "global".to_string());

            Ok(encrypt::BackendSpec::gcp_kms(
                project_id.clone(),
                key_ring.clone(),
                crypto_key.clone(),
                location,
            ))
        }
        "azurekms" => {
            let vault_name = config.get("vault_name").ok_or_else(|| {
                crate::error::ErrorWrapper::new(
                    "Azure KMS backend requires vault_name in encryption_config".to_string(),
                )
            })?;

            let tenant_id = config.get("tenant_id").ok_or_else(|| {
                crate::error::ErrorWrapper::new(
                    "Azure KMS backend requires tenant_id in encryption_config".to_string(),
                )
            })?;

            Ok(encrypt::BackendSpec::azure_kms(
                vault_name.clone(),
                tenant_id.clone(),
            ))
        }
        backend => Err(crate::error::ErrorWrapper::new(format!(
            "Unknown encryption backend: {}",
            backend
        ))),
    }
}

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

    fn make_field(
        backend: Option<&str>,
        config: Option<HashMap<String, String>>,
    ) -> FieldDefinition {
        FieldDefinition {
            name: "test".to_string(),
            field_type: crate::form_parser::FieldType::Text,
            prompt: "Test: ".to_string(),
            default: None,
            placeholder: None,
            options: Vec::new(),
            options_from: None,
            required: None,
            file_extension: None,
            prefix_text: None,
            page_size: None,
            vim_mode: None,
            display_mode: None,
            searchable: None,
            min_selected: None,
            max_selected: None,
            custom_type: None,
            min_date: None,
            max_date: None,
            week_start: None,
            order: 0,
            when: None,
            when_false: Default::default(),
            i18n: None,
            group: None,
            nickel_contract: None,
            nickel_path: None,
            nickel_doc: None,
            nickel_alias: None,
            fragment: None,
            min_items: None,
            max_items: None,
            default_items: None,
            unique: None,
            unique_key: None,
            sensitive: None,
            encryption_backend: backend.map(String::from),
            encryption_config: config,
        }
    }

    #[test]
    fn test_age_default() {
        let field = make_field(Some("age"), None);
        let spec = field_to_backend_spec(&field, None).unwrap();
        assert_eq!(spec.backend_name(), "age");
    }

    #[test]
    fn test_age_custom_key_path() {
        let mut config = HashMap::new();
        config.insert("key_file".to_string(), "/custom/key.txt".to_string());

        let field = make_field(Some("age"), Some(config));
        let spec = field_to_backend_spec(&field, None).unwrap();
        assert_eq!(spec.backend_name(), "age");
    }

    #[test]
    fn test_sops() {
        let field = make_field(Some("sops"), None);
        let spec = field_to_backend_spec(&field, None).unwrap();
        assert_eq!(spec.backend_name(), "sops");
    }

    #[test]
    fn test_default_backend_fallback() {
        let field = make_field(None, None);
        let spec = field_to_backend_spec(&field, Some("age")).unwrap();
        assert_eq!(spec.backend_name(), "age");
    }

    #[test]
    fn test_no_backend_uses_age() {
        let field = make_field(None, None);
        let spec = field_to_backend_spec(&field, None).unwrap();
        assert_eq!(spec.backend_name(), "age");
    }

    #[test]
    fn test_secretumvault_missing_config() {
        let field = make_field(Some("secretumvault"), None);
        let result = field_to_backend_spec(&field, None);
        assert!(result.is_err());
    }

    #[test]
    fn test_secretumvault_with_config() {
        let mut config = HashMap::new();
        config.insert("vault_addr".to_string(), "https://vault:8200".to_string());
        config.insert("vault_token".to_string(), "token".to_string());
        config.insert("key_name".to_string(), "app-key".to_string());

        let field = make_field(Some("secretumvault"), Some(config));
        let spec = field_to_backend_spec(&field, None).unwrap();
        assert_eq!(spec.backend_name(), "secretumvault");
    }

    #[test]
    fn test_awskms_with_config() {
        let mut config = HashMap::new();
        config.insert("region".to_string(), "us-east-1".to_string());
        config.insert("key_id".to_string(), "arn:aws:kms:...".to_string());

        let field = make_field(Some("awskms"), Some(config));
        let spec = field_to_backend_spec(&field, None).unwrap();
        assert_eq!(spec.backend_name(), "awskms");
    }

    #[test]
    fn test_unknown_backend() {
        let field = make_field(Some("unknown"), None);
        let result = field_to_backend_spec(&field, None);
        assert!(result.is_err());
    }
}
