#![allow(clippy::too_many_lines)]

use crate::aaml::AAML;
use crate::error::AamlError;
use crate::types::Type;
use crate::types::primitive_type::PrimitiveType;
use std::fmt;

pub enum PhysicsTypes {
    Meter,
    Kilogram,
    Second,
    Ampere,
    Kelvin,
    Mole,
    Candela,
    SquareMeter,
    CubicMeter,
    MeterPerSecond,
    MeterPerSecondSquared,
    RadianPerSecond,
    RadianPerSecondSquared,
    Hertz,
    KilogramPerCubicMeter,
    KilogramMeterPerSecond,
    Newton,
    NewtonMeter,
    Pascal,
    Joule,
    Watt,
    NewtonPerMeter,
    Dimensionless,
    KilogramSquareMeter,
    JoulePerKilogramKelvin,
    JoulePerKilogram,
    JoulePerKelvin,
    Coulomb,
    Volt,
    Ohm,
    OhmMeter,
    Farad,
    VoltPerMeter,
    Tesla,
    Weber,
    Henry,
    Dioptre,
    Percentage,
    Becquerel,
    Gray,
    Sievert,
    ElectronVolt,
    Barn,
    LightYear,
    Parsec,
    AstronomicalUnit,
    HubbleConstant,
    Siemens,
    CoulombPerCubicMeter,
    CoulombPerSquareMeter,
    FaradPerMeter,
    HenryPerMeter,
    AmperePerMeter,
    AmperePerSquareMeter,
    VoltPerKelvin,
    PascalSecond,
    SquareMeterPerSecond,
    NewtonSecond,
    NewtonPerCubicMeter,
    JouleSecond,
    KilogramPerMole,
    CubicMeterPerKilogram,
    MeterPerCubicSecond,
    Lumen,
    Lux,
    LumenSecond,
    CandelaPerSquareMeter,
    WattPerSteradian,
    WattPerSquareMeter,
    WattPerMeterKelvin,
    JoulePerSquareMeter,
    Radian,
    Steradian,
    Bit,
    Decibel,
    Katal,
    MolePerCubicMeter,
    NewtonPerMeterSquared,
    JoulePerMole,
    JoulePerMoleKelvin,
    KelvinPerWatt,
    KilogramPerSecond,
    CubicMeterPerSecond,
    InverseMeter,
    NewtonPerCoulomb,
    WeberPerMeter,
    TeslaSquareMeter,
    ArcDegree,
    ArcMinute,
    ArcSecond,
    Bar,
    MillimeterOfMercury,
    Atmosphere,
    Torr,
    Poise,
    Stokes,
    Sverdrup,
    Rayl,
    Gal,
    Maxwell,
    Gauss,
    Oersted,
    Gilbert,
    Franklin,
    Debye,
    Angstrom,
    Lambert,
    Phot,
    Stilb,
    Kayser,
    Calorie,
    BritishThermalUnit,
    Langley,
    Fahrenheit,
    Celsius,
    Rankine,
    Curie,
    Roentgen,
    Rutherford,
    Fermi,
    Dalton,
    Byte,
    Baud,
    Erlang,
    MetabolicEquivalent,
    Jansky,
    MachNumber,
    Knots,
    NauticalMile,
    Horsepower,
}

impl Type for PhysicsTypes {
    fn from_name(name: &str) -> Result<Self, AamlError> {
        match name.to_lowercase().replace(['_', '-'], "").as_str() {
            // Base SI Units
            "meter" => Ok(Self::Meter),
            "kilogram" => Ok(Self::Kilogram),
            "second" => Ok(Self::Second),
            "ampere" => Ok(Self::Ampere),
            "kelvin" => Ok(Self::Kelvin),
            "mole" => Ok(Self::Mole),
            "candela" => Ok(Self::Candela),

            // Geometry and Space
            "squaremeter" => Ok(Self::SquareMeter),
            "cubicmeter" => Ok(Self::CubicMeter),
            "radian" => Ok(Self::Radian),
            "steradian" => Ok(Self::Steradian),
            "arcdegree" => Ok(Self::ArcDegree),
            "arcminute" => Ok(Self::ArcMinute),
            "arcsecond" => Ok(Self::ArcSecond),
            "angstrom" => Ok(Self::Angstrom),
            "inversemeter" => Ok(Self::InverseMeter),

            // Mechanics and Motion
            "meterpersecond" => Ok(Self::MeterPerSecond),
            "meterpersecondsquared" => Ok(Self::MeterPerSecondSquared),
            "radianpersecond" => Ok(Self::RadianPerSecond),
            "radianpersecondsquared" => Ok(Self::RadianPerSecondSquared),
            "newton" => Ok(Self::Newton),
            "newtonmeter" => Ok(Self::NewtonMeter),
            "pascal" => Ok(Self::Pascal),
            "joule" => Ok(Self::Joule),
            "watt" => Ok(Self::Watt),
            "hertz" => Ok(Self::Hertz),
            "kilogrampercubicmeter" => Ok(Self::KilogramPerCubicMeter),
            "kilogrammeterpersecond" => Ok(Self::KilogramMeterPerSecond),
            "newtonpermeter" => Ok(Self::NewtonPerMeter),
            "kilogramsquaremeter" => Ok(Self::KilogramSquareMeter),
            "pascalsecond" => Ok(Self::PascalSecond),
            "squaremeterpersecond" => Ok(Self::SquareMeterPerSecond),
            "newtonsecond" => Ok(Self::NewtonSecond),
            "newtonpercubicmeter" => Ok(Self::NewtonPerCubicMeter),
            "joulesecond" => Ok(Self::JouleSecond),
            "meterpercubicsecond" => Ok(Self::MeterPerCubicSecond),
            "kilogrampersecond" => Ok(Self::KilogramPerSecond),
            "cubicmeterpersecond" => Ok(Self::CubicMeterPerSecond),
            "newtonpermetersquared" => Ok(Self::NewtonPerMeterSquared),

            // Electricity and Magnetism
            "coulomb" => Ok(Self::Coulomb),
            "volt" => Ok(Self::Volt),
            "ohm" => Ok(Self::Ohm),
            "ohmmeter" => Ok(Self::OhmMeter),
            "farad" => Ok(Self::Farad),
            "voltpermeter" => Ok(Self::VoltPerMeter),
            "tesla" => Ok(Self::Tesla),
            "weber" => Ok(Self::Weber),
            "henry" => Ok(Self::Henry),
            "siemens" => Ok(Self::Siemens),
            "coulombpercubicmeter" => Ok(Self::CoulombPerCubicMeter),
            "coulombpersquaremeter" => Ok(Self::CoulombPerSquareMeter),
            "faradpermeter" => Ok(Self::FaradPerMeter),
            "henrypermeter" => Ok(Self::HenryPerMeter),
            "amperepermeter" => Ok(Self::AmperePerMeter),
            "amperepersquaremeter" => Ok(Self::AmperePerSquareMeter),
            "newtonpercoulomb" => Ok(Self::NewtonPerCoulomb),
            "weberpermeter" => Ok(Self::WeberPerMeter),
            "teslasquaremeter" => Ok(Self::TeslaSquareMeter),

            // Thermodynamics
            "jouleperkilogramkelvin" => Ok(Self::JoulePerKilogramKelvin),
            "jouleperkilogram" => Ok(Self::JoulePerKilogram),
            "jouleperkelvin" => Ok(Self::JoulePerKelvin),
            "voltperkelvin" => Ok(Self::VoltPerKelvin),
            "wattpermeterkelvin" => Ok(Self::WattPerMeterKelvin),
            "joulepermolekelvin" => Ok(Self::JoulePerMoleKelvin),
            "kelvinperwatt" => Ok(Self::KelvinPerWatt),
            "celsius" => Ok(Self::Celsius),
            "fahrenheit" => Ok(Self::Fahrenheit),
            "rankine" => Ok(Self::Rankine),

            // Chemistry and Particles
            "kilogrampermole" => Ok(Self::KilogramPerMole),
            "cubicmeterperkilogram" => Ok(Self::CubicMeterPerKilogram),
            "katal" => Ok(Self::Katal),
            "molepercubicmeter" => Ok(Self::MolePerCubicMeter),
            "joulepermole" => Ok(Self::JoulePerMole),
            "atomicmassunit" | "dalton" => Ok(Self::Dalton), // Dalton is a synonym for amu
            "barn" => Ok(Self::Barn),

            // Optics and Radiation
            "dioptre" => Ok(Self::Dioptre),
            "becquerel" => Ok(Self::Becquerel),
            "gray" => Ok(Self::Gray),
            "sievert" => Ok(Self::Sievert),
            "electronvolt" => Ok(Self::ElectronVolt),
            "lumen" => Ok(Self::Lumen),
            "lux" => Ok(Self::Lux),
            "lumensecond" => Ok(Self::LumenSecond),
            "candelapersquaremeter" => Ok(Self::CandelaPerSquareMeter),
            "wattpersteradian" => Ok(Self::WattPerSteradian),
            "wattpersquaremeter" => Ok(Self::WattPerSquareMeter),
            "joulepersquaremeter" => Ok(Self::JoulePerSquareMeter),
            "curie" => Ok(Self::Curie),
            "roentgen" => Ok(Self::Roentgen),
            "rutherford" => Ok(Self::Rutherford),

            // Astronomy
            "lightyear" => Ok(Self::LightYear),
            "parsec" => Ok(Self::Parsec),
            "astronomicalunit" => Ok(Self::AstronomicalUnit),
            "hubbleconstant" => Ok(Self::HubbleConstant),
            "jansky" => Ok(Self::Jansky),

            // Computing
            "bit" => Ok(Self::Bit),
            "byte" => Ok(Self::Byte),
            "baud" => Ok(Self::Baud),
            "erlang" => Ok(Self::Erlang),

            // Miscellaneous and Non-SI Units
            "dimensionless" => Ok(Self::Dimensionless),
            "percentage" => Ok(Self::Percentage),
            "decibel" => Ok(Self::Decibel),
            "bar" => Ok(Self::Bar),
            "millimeterofmercury" => Ok(Self::MillimeterOfMercury),
            "atmosphere" => Ok(Self::Atmosphere),
            "torr" => Ok(Self::Torr),
            "poise" => Ok(Self::Poise),
            "stokes" => Ok(Self::Stokes),
            "sverdrup" => Ok(Self::Sverdrup),
            "rayl" => Ok(Self::Rayl),
            "gal" => Ok(Self::Gal),
            "maxwell" => Ok(Self::Maxwell),
            "gauss" => Ok(Self::Gauss),
            "oersted" => Ok(Self::Oersted),
            "gilbert" => Ok(Self::Gilbert),
            "franklin" => Ok(Self::Franklin),
            "debye" => Ok(Self::Debye),
            "lambert" => Ok(Self::Lambert),
            "phot" => Ok(Self::Phot),
            "stilb" => Ok(Self::Stilb),
            "kayser" => Ok(Self::Kayser),
            "calorie" => Ok(Self::Calorie),
            "britishthermalunit" => Ok(Self::BritishThermalUnit),
            "langley" => Ok(Self::Langley),
            "fermi" => Ok(Self::Fermi),
            "metabolicequivalent" => Ok(Self::MetabolicEquivalent),
            "machnumber" => Ok(Self::MachNumber),
            "knots" => Ok(Self::Knots),
            "nauticalmile" => Ok(Self::NauticalMile),
            "horsepower" => Ok(Self::Horsepower),

            _ => Err(AamlError::NotFound {
                key: name.to_string(),
                context: "physics types".to_string(),
                diagnostics: Some(Box::new(crate::error::ErrorDiagnostics::new(
                    "Unknown physics type",
                    format!("Physics type '{name}' is not recognized"),
                    "Check the type name or use a built-in physics unit",
                ))),
            }),
        }
    }

    /// Returns the recommended primitive type for storing the value of this unit.
    fn base_type(&self) -> PrimitiveType {
        match self {
            // Units typically represented as integers (counting, data sizes)
            Self::Bit | Self::Byte | Self::Baud => PrimitiveType::I32,

            // Most physical quantities require high precision floating point
            _ => PrimitiveType::F64,
        }
    }

    fn validate(&self, value: &str, _aaml: &AAML) -> Result<(), AamlError> {
        match self.base_type() {
            PrimitiveType::I32 => {
                value.parse::<i32>().map_err(|_| AamlError::InvalidValue {
                    details: format!("'{value}' is not a valid integer"),
                    expected: "integer value".to_string(),
                    diagnostics: Some(Box::new(crate::error::ErrorDiagnostics::new(
                        "Invalid unit value",
                        format!("Unit {self} requires an integer, got '{value}'"),
                        "Use integer notation: 42, -100, etc.",
                    ))),
                })?;
            }
            PrimitiveType::F64 => {
                value.parse::<f64>().map_err(|_| AamlError::InvalidValue {
                    details: format!("'{value}' is not a valid number"),
                    expected: "floating-point number".to_string(),
                    diagnostics: Some(Box::new(crate::error::ErrorDiagnostics::new(
                        "Invalid unit value",
                        format!("Unit {self} requires a number, got '{value}'"),
                        "Use numeric notation: 3.14, 2.0, -1.5, etc.",
                    ))),
                })?;
            }
            _ => {
                return Err(AamlError::InvalidValue {
                    details: format!("Unit {self} has unsupported base type"),
                    expected: "i32 or f64".to_string(),
                    diagnostics: Some(Box::new(crate::error::ErrorDiagnostics::new(
                        "Unsupported unit type",
                        format!("Cannot validate unit {self} with current base type"),
                        "Check the unit definition",
                    ))),
                });
            }
        }
        Ok(())
    }
}

impl fmt::Display for PhysicsTypes {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let s = match self {
            Self::Meter => "meter",
            Self::Kilogram => "kilogram",
            Self::Second => "second",
            Self::Ampere => "ampere",
            Self::Kelvin => "kelvin",
            Self::Mole => "mole",
            Self::Candela => "candela",
            Self::SquareMeter => "squareMeter",
            Self::CubicMeter => "cubicMeter",
            Self::MeterPerSecond => "meterPerSecond",
            Self::MeterPerSecondSquared => "meterPerSecondSquared",
            Self::RadianPerSecond => "radianPerSecond",
            Self::RadianPerSecondSquared => "radianPerSecondSquared",
            Self::Hertz => "hertz",
            Self::KilogramPerCubicMeter => "kilogramPerCubicMeter",
            Self::KilogramMeterPerSecond => "kilogramMeterPerSecond",
            Self::Newton => "newton",
            Self::NewtonMeter => "newtonMeter",
            Self::Pascal => "pascal",
            Self::Joule => "joule",
            Self::Watt => "watt",
            Self::NewtonPerMeter => "newtonPerMeter",
            Self::Dimensionless => "dimensionless",
            Self::KilogramSquareMeter => "kilogramSquareMeter",
            Self::JoulePerKilogramKelvin => "joulePerKilogramKelvin",
            Self::JoulePerKilogram => "joulePerKilogram",
            Self::JoulePerKelvin => "joulePerKelvin",
            Self::Coulomb => "coulomb",
            Self::Volt => "volt",
            Self::Ohm => "ohm",
            Self::OhmMeter => "ohmMeter",
            Self::Farad => "farad",
            Self::VoltPerMeter => "voltPerMeter",
            Self::Tesla => "tesla",
            Self::Weber => "weber",
            Self::Henry => "henry",
            Self::Dioptre => "dioptre",
            Self::Percentage => "percentage",
            Self::Becquerel => "becquerel",
            Self::Gray => "gray",
            Self::Sievert => "sievert",
            Self::ElectronVolt => "electronVolt",
            Self::Barn => "barn",
            Self::LightYear => "lightYear",
            Self::Parsec => "parsec",
            Self::AstronomicalUnit => "astronomicalUnit",
            Self::HubbleConstant => "hubbleConstant",
            Self::Siemens => "siemens",
            Self::CoulombPerCubicMeter => "coulombPerCubicMeter",
            Self::CoulombPerSquareMeter => "coulombPerSquareMeter",
            Self::FaradPerMeter => "faradPerMeter",
            Self::HenryPerMeter => "henryPerMeter",
            Self::AmperePerMeter => "amperePerMeter",
            Self::AmperePerSquareMeter => "amperePerSquareMeter",
            Self::VoltPerKelvin => "voltPerKelvin",
            Self::PascalSecond => "pascalSecond",
            Self::SquareMeterPerSecond => "squareMeterPerSecond",
            Self::NewtonSecond => "newtonSecond",
            Self::NewtonPerCubicMeter => "newtonPerCubicMeter",
            Self::JouleSecond => "jouleSecond",
            Self::KilogramPerMole => "kilogramPerMole",
            Self::CubicMeterPerKilogram => "cubicMeterPerKilogram",
            Self::MeterPerCubicSecond => "meterPerCubicSecond",
            Self::Lumen => "lumen",
            Self::Lux => "lux",
            Self::LumenSecond => "lumenSecond",
            Self::CandelaPerSquareMeter => "candelaPerSquareMeter",
            Self::WattPerSteradian => "wattPerSteradian",
            Self::WattPerSquareMeter => "wattPerSquareMeter",
            Self::WattPerMeterKelvin => "wattPerMeterKelvin",
            Self::JoulePerSquareMeter => "joulePerSquareMeter",
            Self::Radian => "radian",
            Self::Steradian => "steradian",
            Self::Bit => "bit",
            Self::Decibel => "decibel",
            Self::Katal => "katal",
            Self::MolePerCubicMeter => "molePerCubicMeter",
            Self::NewtonPerMeterSquared => "newtonPerMeterSquared",
            Self::JoulePerMole => "joulePerMole",
            Self::JoulePerMoleKelvin => "joulePerMoleKelvin",
            Self::KelvinPerWatt => "kelvinPerWatt",
            Self::KilogramPerSecond => "kilogramPerSecond",
            Self::CubicMeterPerSecond => "cubicMeterPerSecond",
            Self::InverseMeter => "inverseMeter",
            Self::NewtonPerCoulomb => "newtonPerCoulomb",
            Self::WeberPerMeter => "weberPerMeter",
            Self::TeslaSquareMeter => "teslaSquareMeter",
            Self::ArcDegree => "arcDegree",
            Self::ArcMinute => "arcMinute",
            Self::ArcSecond => "arcSecond",
            Self::Bar => "bar",
            Self::MillimeterOfMercury => "millimeterOfMercury",
            Self::Atmosphere => "atmosphere",
            Self::Torr => "torr",
            Self::Poise => "poise",
            Self::Stokes => "stokes",
            Self::Sverdrup => "sverdrup",
            Self::Rayl => "rayl",
            Self::Gal => "gal",
            Self::Maxwell => "maxwell",
            Self::Gauss => "gauss",
            Self::Oersted => "oersted",
            Self::Gilbert => "gilbert",
            Self::Franklin => "franklin",
            Self::Debye => "debye",
            Self::Angstrom => "angstrom",
            Self::Lambert => "lambert",
            Self::Phot => "phot",
            Self::Stilb => "stilb",
            Self::Kayser => "kayser",
            Self::Calorie => "calorie",
            Self::BritishThermalUnit => "britishThermalUnit",
            Self::Langley => "langley",
            Self::Fahrenheit => "fahrenheit",
            Self::Celsius => "celsius",
            Self::Rankine => "rankine",
            Self::Curie => "curie",
            Self::Roentgen => "roentgen",
            Self::Rutherford => "rutherford",
            Self::Fermi => "fermi",
            Self::Dalton => "dalton",
            Self::Byte => "byte",
            Self::Baud => "baud",
            Self::Erlang => "erlang",
            Self::MetabolicEquivalent => "metabolicEquivalent",
            Self::Jansky => "jansky",
            Self::MachNumber => "machNumber",
            Self::Knots => "knots",
            Self::NauticalMile => "nauticalMile",
            Self::Horsepower => "horsepower",
        };
        write!(f, "{s}")
    }
}
