use std::{hash::Hash, str::FromStr};

use chrono::{NaiveDate, Utc};
use indexmap::IndexMap;
#[cfg(feature = "schemars")]
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::{
    Country, Currency, Language, Links, PriceList, fuse::MainFuseSizes,
    price_list::PriceListSimplified, registry::sweden,
};

#[derive(Debug, Clone)]
pub struct GridOperator {
    name: &'static str,
    vat_number: &'static str,
    /// Costs are specified in this currency
    country: Country,
    /// The main fuse size range that this info covers
    main_fuses: MainFuseSizes,
    price_lists: &'static [PriceList],
    links: Links,
}

impl GridOperator {
    pub const fn name(&self) -> &str {
        self.name
    }

    pub const fn vat_number(&self) -> &str {
        self.vat_number
    }

    pub const fn country(&self) -> Country {
        self.country
    }

    pub const fn links(&self) -> &Links {
        &self.links
    }

    pub fn all_price_lists(&self) -> &'static [PriceList] {
        self.price_lists
    }

    pub fn price_lists(&self, date: NaiveDate) -> Vec<&'static PriceList> {
        let mut map: IndexMap<Option<&str>, &PriceList> = IndexMap::new();
        for pl in self.price_lists {
            if date >= pl.from_date() {
                if let Some(current_max_date) = map.get(&pl.variant()).map(|pl| pl.from_date()) {
                    if pl.from_date() > current_max_date {
                        map.insert(pl.variant(), pl);
                    }
                } else {
                    map.insert(pl.variant(), pl);
                }
            }
        }
        map.into_values().collect()
    }

    pub fn price_list(&self, variant: Option<&str>, date: NaiveDate) -> Option<&'static PriceList> {
        self.price_lists(date)
            .iter()
            .filter(|pl| pl.variant() == variant)
            .next_back()
            .copied()
    }

    pub fn current_price_lists(&self) -> Vec<&'static PriceList> {
        let now = Utc::now().date_naive();
        self.price_lists(now)
    }

    pub fn current_price_list(&self, variant: Option<&str>) -> Option<&'static PriceList> {
        let now = Utc::now().date_naive();
        self.price_list(variant, now)
    }

    pub const fn currency(&self) -> Currency {
        match self.country {
            Country::SE => Currency::SEK,
        }
    }

    pub fn get(country: Country, name: &str) -> Option<&'static Self> {
        match country {
            Country::SE => sweden::GRID_OPERATORS
                .iter()
                .find(|o| o.name == name)
                .copied(),
        }
    }

    pub fn get_by_vat_id(vat_id: &str) -> Option<&'static Self> {
        let country: Country = vat_id[0..2].parse().ok()?;
        match country {
            Country::SE => sweden::GRID_OPERATORS
                .iter()
                .find(|o| o.vat_number == vat_id)
                .copied(),
        }
    }

    pub fn all() -> Vec<&'static Self> {
        sweden::GRID_OPERATORS.to_vec()
    }

    pub fn all_for_country(country: Country) -> &'static [&'static Self] {
        match country {
            Country::SE => sweden::GRID_OPERATORS,
        }
    }

    pub const fn builder() -> GridOperatorBuilder {
        GridOperatorBuilder::new()
    }

    pub fn simplified(
        &self,
        fuse_size: u16,
        yearly_consumption: u32,
        language: Language,
    ) -> GridOperatorSimplified {
        GridOperatorSimplified::new(self, fuse_size, yearly_consumption, language)
    }
}

/// Grid operator with only current prices
#[derive(Debug, Clone, Serialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct GridOperatorSimplified {
    name: &'static str,
    vat_number: &'static str,
    /// Costs are specified in this currency
    country: Country,
    price_lists: Vec<PriceListSimplified>,
}

impl GridOperatorSimplified {
    pub fn name(&self) -> &'static str {
        self.name
    }

    pub fn vat_number(&self) -> &'static str {
        self.vat_number
    }

    pub fn country(&self) -> Country {
        self.country
    }

    pub fn price_lists(&self) -> &[PriceListSimplified] {
        &self.price_lists
    }
}

impl GridOperatorSimplified {
    fn new(op: &GridOperator, fuse_size: u16, yearly_consumption: u32, language: Language) -> Self {
        Self {
            name: op.name,
            vat_number: op.vat_number,
            country: op.country(),
            price_lists: op
                .current_price_lists()
                .into_iter()
                .map(|pl| pl.simplified(fuse_size, yearly_consumption, language))
                .collect(),
        }
    }
}

#[derive(Debug, Clone)]
pub struct GridOperatorBuilder {
    name: Option<&'static str>,
    vat_number: Option<&'static str>,
    /// Costs are specified in this currency
    country: Option<Country>,
    /// The main fuse size range that this info covers
    main_fuses: Option<MainFuseSizes>,
    price_lists: Option<&'static [PriceList]>,
    links: Option<Links>,
}

impl Default for GridOperatorBuilder {
    fn default() -> Self {
        Self::new()
    }
}

impl GridOperatorBuilder {
    pub const fn new() -> Self {
        Self {
            name: None,
            vat_number: None,
            country: None,
            main_fuses: None,
            price_lists: None,
            links: None,
        }
    }

    pub const fn name(mut self, name: &'static str) -> Self {
        self.name = Some(name);
        self
    }

    pub const fn vat_number(mut self, vat_number: &'static str) -> Self {
        self.vat_number = Some(vat_number);
        self
    }

    pub const fn country(mut self, country: Country) -> Self {
        self.country = Some(country);
        self
    }

    pub const fn main_fuses(mut self, main_fuses: MainFuseSizes) -> Self {
        self.main_fuses = Some(main_fuses);
        self
    }

    pub const fn links(mut self, links: Links) -> Self {
        self.links = Some(links);
        self
    }

    pub const fn price_lists(mut self, price_lists: &'static [PriceList]) -> Self {
        self.price_lists = Some(price_lists);
        self
    }

    pub const fn build(self) -> GridOperator {
        GridOperator {
            name: self.name.expect("`name` required"),
            vat_number: self.vat_number.expect("`vat_number` required"),
            country: self.country.expect("`country` required"),
            main_fuses: self.main_fuses.expect("`main_fuses` required"),
            price_lists: self.price_lists.expect("`price_lists` expected"),
            links: self.links.expect("`links` required"),
        }
    }
}

impl FromStr for &'static GridOperator {
    type Err = &'static str;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        GridOperator::get_by_vat_id(s).ok_or("grid operator not found")
    }
}

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

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

#[cfg(feature = "schemars")]
impl JsonSchema for GridOperator {
    fn schema_name() -> std::borrow::Cow<'static, str> {
        "GridOperator".into()
    }

    fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
        String::json_schema(generator)
    }
}

impl PartialEq for GridOperator {
    fn eq(&self, other: &Self) -> bool {
        self.vat_number == other.vat_number
    }
}

impl Eq for GridOperator {}

impl PartialOrd for GridOperator {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for GridOperator {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.vat_number.cmp(other.vat_number)
    }
}

impl Hash for GridOperator {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.vat_number.hash(state);
    }
}

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

    #[test]
    fn test_grid_operator_serialize_deserialize() {
        let operator: &GridOperator = "SE556037732601".parse().unwrap();

        // Serialize to JSON string
        let serialized = serde_json::to_string(operator).expect("serialization should succeed");

        // The serialized value should be the VAT number wrapped in quotes
        assert_eq!(serialized, format!("\"{}\"", operator.vat_number()));

        // Deserialize back to GridOperator reference
        let deserialized: &'static GridOperator =
            serde_json::from_str(&serialized).expect("deserialization should succeed");

        // Verify the deserialized operator matches the original
        assert_eq!(deserialized.name(), operator.name());
        assert_eq!(deserialized.vat_number(), operator.vat_number());
        assert_eq!(deserialized.country(), operator.country());
    }

    #[test]
    fn test_grid_operator_deserialize_invalid() {
        // Try to deserialize an invalid VAT number
        let result: Result<&'static GridOperator, _> = serde_json::from_str("\"SE000000000000\"");

        assert!(
            result.is_err(),
            "deserialization should fail for invalid VAT number"
        );
    }

    #[test]
    fn test_grid_operator_partial_eq() {
        let operator1: &GridOperator = "SE556037732601".parse().unwrap();
        let operator2: &GridOperator = "SE556037732601".parse().unwrap();
        let operator3: &GridOperator = "SE556532083401".parse().unwrap();

        // Test equality - same VAT number
        assert_eq!(operator1, operator2);

        // Test inequality - different VAT numbers
        assert_ne!(operator1, operator3);
    }

    #[test]
    fn test_grid_operator_partial_ord() {
        let operator1: &GridOperator = "SE556037732601".parse().unwrap();
        let operator2: &GridOperator = "SE556532083401".parse().unwrap();

        // Test ordering based on VAT number
        assert!(operator1 < operator2);
        assert!(operator2 > operator1);
        assert!(operator1 <= operator1);
        assert!(operator1 >= operator1);
    }

    #[test]
    fn test_grid_operator_ord() {
        let operator1: &GridOperator = "SE556037732601".parse().unwrap();
        let operator2: &GridOperator = "SE556532083401".parse().unwrap();
        let operator3: &GridOperator = "SE556037732601".parse().unwrap();

        // Test Ord::cmp
        assert_eq!(operator1.cmp(operator2), std::cmp::Ordering::Less);
        assert_eq!(operator2.cmp(operator1), std::cmp::Ordering::Greater);
        assert_eq!(operator1.cmp(operator3), std::cmp::Ordering::Equal);
    }

    #[test]
    fn test_grid_operator_hash() {
        use std::collections::hash_map::DefaultHasher;
        use std::hash::{Hash, Hasher};

        let operator1: &GridOperator = "SE556037732601".parse().unwrap();
        let operator2: &GridOperator = "SE556037732601".parse().unwrap();
        let operator3: &GridOperator = "SE556532083401".parse().unwrap();

        // Helper function to compute hash
        let compute_hash = |op: &GridOperator| {
            let mut hasher = DefaultHasher::new();
            op.hash(&mut hasher);
            hasher.finish()
        };

        // Operators with same VAT number should have same hash
        assert_eq!(compute_hash(operator1), compute_hash(operator2));

        // Operators with different VAT numbers should have different hashes
        assert_ne!(compute_hash(operator1), compute_hash(operator3));
    }
}
