use crate::types::validation_errors::ValidationError;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt;
use std::str::FromStr;

/// Código do país (ISO 3166-2)
///
/// Enum com os países mais comuns e suporte para códigos personalizados.
/// Fornece autocompletar e type-safety enquanto mantém flexibilidade.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Country {
    // América do Sul
    /// Brasil
    BR,
    /// Argentina
    AR,
    /// Chile
    CL,
    /// Colômbia
    CO,
    /// Peru
    PE,
    /// Uruguai
    UY,
    /// Paraguai
    PY,
    /// Venezuela
    VE,
    /// Equador
    EC,
    /// Bolívia
    BO,

    // América do Norte
    /// Estados Unidos
    US,
    /// Canadá
    CA,
    /// México
    MX,

    // Europa
    /// Reino Unido
    GB,
    /// Alemanha
    DE,
    /// França
    FR,
    /// Itália
    IT,
    /// Espanha
    ES,
    /// Portugal
    PT,
    /// Holanda
    NL,
    /// Suíça
    CH,
    /// Bélgica
    BE,
    /// Áustria
    AT,
    /// Suécia
    SE,
    /// Noruega
    NO,
    /// Dinamarca
    DK,
    /// Finlândia
    FI,
    /// Polônia
    PL,
    /// Irlanda
    IE,

    // Ásia
    /// China
    CN,
    /// Japão
    JP,
    /// Índia
    IN,
    /// Coreia do Sul
    KR,
    /// Singapura
    SG,
    /// Hong Kong
    HK,
    /// Taiwan
    TW,
    /// Tailândia
    TH,
    /// Malásia
    MY,
    /// Indonésia
    ID,
    /// Filipinas
    PH,
    /// Vietnã
    VN,

    // Oceania
    /// Austrália
    AU,
    /// Nova Zelândia
    NZ,

    // África
    /// África do Sul
    ZA,

    // Oriente Médio
    /// Israel
    IL,
    /// Emirados Árabes Unidos
    AE,

    /// Outro país (código ISO 3166-2 personalizado)
    Other(String),
}

impl Country {
    /// Cria um país a partir do código ISO 3166-2
    ///
    /// # Exemplos
    /// ```
    /// #  use konduto::Country;
    /// let br = Country::from_code("BR").unwrap();
    /// let us = Country::from_code("us").unwrap(); // case-insensitive
    /// let custom = Country::from_code("XX").unwrap(); // Aceita códigos não listados
    /// ```
    pub fn from_code(code: impl Into<String>) -> Result<Self, ValidationError> {
        let input = code.into();
        let trimmed = input.trim().to_uppercase();

        if trimmed.is_empty() {
            return Err(ValidationError::EmptyField("country".to_string()));
        }

        if trimmed.len() != 2 {
            return Err(ValidationError::InvalidFormat {
                field: "country".to_string(),
                message: format!(
                    "country code must be exactly 2 characters (ISO 3166-2), received {}",
                    trimmed.len()
                ),
            });
        }

        if !trimmed.chars().all(|c| c.is_ascii_alphabetic()) {
            return Err(ValidationError::InvalidFormat {
                field: "country".to_string(),
                message: "country code must contain only alphabetic characters".to_string(),
            });
        }

        Ok(match trimmed.as_str() {
            // América do Sul
            "BR" => Country::BR,
            "AR" => Country::AR,
            "CL" => Country::CL,
            "CO" => Country::CO,
            "PE" => Country::PE,
            "UY" => Country::UY,
            "PY" => Country::PY,
            "VE" => Country::VE,
            "EC" => Country::EC,
            "BO" => Country::BO,

            // América do Norte
            "US" => Country::US,
            "CA" => Country::CA,
            "MX" => Country::MX,

            // Europa
            "GB" => Country::GB,
            "DE" => Country::DE,
            "FR" => Country::FR,
            "IT" => Country::IT,
            "ES" => Country::ES,
            "PT" => Country::PT,
            "NL" => Country::NL,
            "CH" => Country::CH,
            "BE" => Country::BE,
            "AT" => Country::AT,
            "SE" => Country::SE,
            "NO" => Country::NO,
            "DK" => Country::DK,
            "FI" => Country::FI,
            "PL" => Country::PL,
            "IE" => Country::IE,

            // Ásia
            "CN" => Country::CN,
            "JP" => Country::JP,
            "IN" => Country::IN,
            "KR" => Country::KR,
            "SG" => Country::SG,
            "HK" => Country::HK,
            "TW" => Country::TW,
            "TH" => Country::TH,
            "MY" => Country::MY,
            "ID" => Country::ID,
            "PH" => Country::PH,
            "VN" => Country::VN,

            // Oceania
            "AU" => Country::AU,
            "NZ" => Country::NZ,

            // África
            "ZA" => Country::ZA,

            // Oriente Médio
            "IL" => Country::IL,
            "AE" => Country::AE,

            // Outros
            code => Country::Other(code.to_string()),
        })
    }

    /// Retorna o código ISO 3166-2 do país
    pub fn code(&self) -> &str {
        match self {
            Country::BR => "BR",
            Country::AR => "AR",
            Country::CL => "CL",
            Country::CO => "CO",
            Country::PE => "PE",
            Country::UY => "UY",
            Country::PY => "PY",
            Country::VE => "VE",
            Country::EC => "EC",
            Country::BO => "BO",
            Country::US => "US",
            Country::CA => "CA",
            Country::MX => "MX",
            Country::GB => "GB",
            Country::DE => "DE",
            Country::FR => "FR",
            Country::IT => "IT",
            Country::ES => "ES",
            Country::PT => "PT",
            Country::NL => "NL",
            Country::CH => "CH",
            Country::BE => "BE",
            Country::AT => "AT",
            Country::SE => "SE",
            Country::NO => "NO",
            Country::DK => "DK",
            Country::FI => "FI",
            Country::PL => "PL",
            Country::IE => "IE",
            Country::CN => "CN",
            Country::JP => "JP",
            Country::IN => "IN",
            Country::KR => "KR",
            Country::SG => "SG",
            Country::HK => "HK",
            Country::TW => "TW",
            Country::TH => "TH",
            Country::MY => "MY",
            Country::ID => "ID",
            Country::PH => "PH",
            Country::VN => "VN",
            Country::AU => "AU",
            Country::NZ => "NZ",
            Country::ZA => "ZA",
            Country::IL => "IL",
            Country::AE => "AE",
            Country::Other(code) => code,
        }
    }

    pub fn name(&self) -> &str {
        match self {
            Country::BR => "Brasil",
            Country::AR => "Argentina",
            Country::CL => "Chile",
            Country::CO => "Colômbia",
            Country::PE => "Peru",
            Country::UY => "Uruguai",
            Country::PY => "Paraguai",
            Country::VE => "Venezuela",
            Country::EC => "Equador",
            Country::BO => "Bolívia",
            Country::US => "Estados Unidos",
            Country::CA => "Canadá",
            Country::MX => "México",
            Country::GB => "Reino Unido",
            Country::DE => "Alemanha",
            Country::FR => "França",
            Country::IT => "Itália",
            Country::ES => "Espanha",
            Country::PT => "Portugal",
            Country::NL => "Holanda",
            Country::CH => "Suíça",
            Country::BE => "Bélgica",
            Country::AT => "Áustria",
            Country::SE => "Suécia",
            Country::NO => "Noruega",
            Country::DK => "Dinamarca",
            Country::FI => "Finlândia",
            Country::PL => "Polônia",
            Country::IE => "Irlanda",
            Country::CN => "China",
            Country::JP => "Japão",
            Country::IN => "Índia",
            Country::KR => "Coreia do Sul",
            Country::SG => "Singapura",
            Country::HK => "Hong Kong",
            Country::TW => "Taiwan",
            Country::TH => "Tailândia",
            Country::MY => "Malásia",
            Country::ID => "Indonésia",
            Country::PH => "Filipinas",
            Country::VN => "Vietnã",
            Country::AU => "Austrália",
            Country::NZ => "Nova Zelândia",
            Country::ZA => "África do Sul",
            Country::IL => "Israel",
            Country::AE => "Emirados Árabes Unidos",
            Country::Other(code) => code,
        }
    }
}

impl FromStr for Country {
    type Err = ValidationError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Country::from_code(s)
    }
}

impl fmt::Display for Country {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.code())
    }
}

impl Serialize for Country {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(self.code())
    }
}

impl<'de> Deserialize<'de> for Country {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        Country::from_code(s).map_err(serde::de::Error::custom)
    }
}

// Mantém alias para compatibilidade
pub type CountryCode = Country;

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

    #[test]
    fn test_brazil() {
        let br = Country::from_code("BR").unwrap();
        assert_eq!(br, Country::BR);
        assert_eq!(br.code(), "BR");
        assert_eq!(br.name(), "Brasil");
    }

    #[test]
    fn test_lowercase_conversion() {
        let us = Country::from_code("us").unwrap();
        assert_eq!(us, Country::US);
        assert_eq!(us.code(), "US");
    }

    #[test]
    fn test_mixed_case() {
        let gb = Country::from_code("Gb").unwrap();
        assert_eq!(gb, Country::GB);
    }

    #[test]
    fn test_with_whitespace() {
        let fr = Country::from_code("  FR  ").unwrap();
        assert_eq!(fr, Country::FR);
    }

    #[test]
    fn test_other_country() {
        let xx = Country::from_code("XX").unwrap();
        assert_eq!(xx, Country::Other("XX".to_string()));
        assert_eq!(xx.code(), "XX");
    }

    #[test]
    fn test_invalid_length() {
        let result = Country::from_code("USA");
        assert!(result.is_err());
    }

    #[test]
    fn test_invalid_chars() {
        let result = Country::from_code("B1");
        assert!(result.is_err());
    }

    #[test]
    fn test_empty() {
        let result = Country::from_code("");
        assert!(result.is_err());
    }

    #[test]
    fn test_serialization() {
        let br = Country::BR;
        let json = serde_json::to_string(&br).unwrap();
        assert_eq!(json, "\"BR\"");
    }

    #[test]
    fn test_deserialization() {
        let json = "\"US\"";
        let country: Country = serde_json::from_str(json).unwrap();
        assert_eq!(country, Country::US);
    }

    #[test]
    fn test_from_str() {
        let br: Country = "BR".parse().unwrap();
        assert_eq!(br, Country::BR);
    }

    #[test]
    fn test_display() {
        let mx = Country::MX;
        assert_eq!(format!("{}", mx), "MX");
    }

    #[test]
    fn test_common_countries() {
        let countries = vec![
            ("BR", "Brasil"),
            ("US", "Estados Unidos"),
            ("GB", "Reino Unido"),
            ("FR", "França"),
            ("DE", "Alemanha"),
            ("IT", "Itália"),
            ("ES", "Espanha"),
            ("JP", "Japão"),
            ("CN", "China"),
        ];

        for (code, expected_name) in countries {
            let country = Country::from_code(code).unwrap();
            assert_eq!(country.code(), code);
            assert_eq!(country.name(), expected_name);
        }
    }

    #[test]
    fn test_equality() {
        let br1 = Country::BR;
        let br2 = Country::from_code("BR").unwrap();
        assert_eq!(br1, br2);
    }

    #[test]
    fn test_hash() {
        use std::collections::HashSet;

        let mut set = HashSet::new();
        set.insert(Country::BR);
        set.insert(Country::US);
        set.insert(Country::BR); // Duplicado

        assert_eq!(set.len(), 2);
    }
}
