use std::ops::{Add, AddAssign, Mul, Sub, SubAssign};

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Default, Clone, Debug, Copy)]
pub struct Euros {
    total_cents: i32,
}

/// should all be converted to euros immediatly after creation
pub enum Currency {
    Euros(f32),
    Nok(f32),
}

impl Currency {
    pub const NOK_RATE: f32 = 0.090;
}

impl TryFrom<String> for Currency {
    type Error = ();

    fn try_from(value: String) -> Result<Self, Self::Error> {
        if let Some(value) = value.strip_prefix("kr") {
            let float = value.parse::<f32>().map_err(|_| ())?;
            Ok(Currency::Nok(float))
        } else {
            let float = value.parse::<f32>().map_err(|_| ())?;
            Ok(Currency::Euros(float))
        }
    }
}

pub struct Kroner(pub f32);

impl From<Currency> for Euros {
    fn from(value: Currency) -> Self {
        match value {
            Currency::Nok(val) => (val * Currency::NOK_RATE).into(),
            Currency::Euros(euros) => euros.into(),
        }
    }
}

impl Euros {
    pub fn new(euros: i32, cents: i32) -> Self {
        Self {
            total_cents: euros * 100 + cents,
        }
    }

    pub fn print(&self) -> String {
        format!("{}.{}", self.euros(), self.cents())
    }

    pub fn as_negative(self) -> Self {
        Self {
            total_cents: -self.total_cents,
        }
    }

    pub fn euros(&self) -> i32 {
        self.total_cents / 100
    }

    pub fn total_cents(&self) -> i32 {
        self.total_cents
    }

    pub fn cents(&self) -> i32 {
        self.total_cents % 100
    }
}

impl From<f32> for Euros {
    fn from(value: f32) -> Self {
        let total_cents = (value * 100.0) as i32;
        Self { total_cents }
    }
}

impl From<Euros> for f32 {
    fn from(value: Euros) -> Self {
        value.total_cents as f32 / 100.0
    }
}

impl Add for Euros {
    type Output = Self;

    fn add(self, other: Self) -> Self {
        Euros {
            total_cents: self.total_cents + other.total_cents,
        }
    }
}

impl Sub for Euros {
    type Output = Self;

    fn sub(self, other: Self) -> Self {
        Euros {
            total_cents: self.total_cents - other.total_cents,
        }
    }
}

impl Mul<i32> for Euros {
    type Output = Self;

    fn mul(self, multiplier: i32) -> Self {
        Euros {
            total_cents: self.total_cents * multiplier,
        }
    }
}

impl SubAssign for Euros {
    fn sub_assign(&mut self, other: Self) {
        self.total_cents -= other.total_cents;
    }
}

impl AddAssign for Euros {
    fn add_assign(&mut self, other: Self) {
        self.total_cents += other.total_cents;
    }
}

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

    #[test]
    fn testing() {
        let kroner = Currency::Nok(100.);

        let euros: Euros = kroner.into();
        dbg!(euros.print());
    }
}