use chrono::NaiveDateTime;
use rex_shared::models::Cent;
use std::fmt::{self, Display};
use strum_macros::{Display, EnumIter};

#[derive(Clone, Debug, Copy, Display, EnumIter)]
pub enum TxType {
    #[strum(to_string = "Income")]
    Income,
    #[strum(to_string = "Expense")]
    Expense,
    #[strum(to_string = "Transfer")]
    Transfer,
    #[strum(to_string = "Borrow")]
    Borrow,
    #[strum(to_string = "Lend")]
    Lend,
    #[strum(to_string = "Borrow Repay")]
    BorrowRepay,
    #[strum(to_string = "Lend Repay")]
    LendRepay,
}

#[derive(Clone, Debug, Copy, Eq, PartialEq)]
pub enum FetchNature {
    Monthly,
    Yearly,
    All,
}

#[derive(Clone, Copy)]
pub enum DateNature {
    Exact(NaiveDateTime),
    ByMonth {
        start_date: NaiveDateTime,
        end_date: NaiveDateTime,
    },
    ByYear {
        start_date: NaiveDateTime,
        end_date: NaiveDateTime,
    },
}

#[derive(Clone, Debug, Copy)]
pub enum AmountNature {
    Exact(Cent),
    MoreThan(Cent),
    MoreThanEqual(Cent),
    LessThan(Cent),
    LessThanEqual(Cent),
}

impl AmountNature {
    #[must_use]
    pub fn extract(&self) -> Cent {
        let i = match self {
            AmountNature::Exact(i)
            | AmountNature::MoreThan(i)
            | AmountNature::MoreThanEqual(i)
            | AmountNature::LessThan(i)
            | AmountNature::LessThanEqual(i) => i,
        };

        *i
    }

    #[must_use]
    pub fn to_type(&self) -> AmountType {
        match self {
            AmountNature::Exact(_) => AmountType::Exact,
            AmountNature::MoreThan(_) => AmountType::MoreThan,
            AmountNature::MoreThanEqual(_) => AmountType::MoreThanEqual,
            AmountNature::LessThan(_) => AmountType::LessThan,
            AmountNature::LessThanEqual(_) => AmountType::LessThanEqual,
        }
    }

    #[must_use]
    pub fn from_type(t: AmountType, amount: Cent) -> Self {
        match t {
            AmountType::Exact => AmountNature::Exact(amount),
            AmountType::MoreThan => AmountNature::MoreThan(amount),
            AmountType::MoreThanEqual => AmountNature::MoreThanEqual(amount),
            AmountType::LessThan => AmountNature::LessThan(amount),
            AmountType::LessThanEqual => AmountNature::LessThanEqual(amount),
        }
    }
}

impl Display for AmountNature {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let a = match self {
            AmountNature::Exact(v) => format!("{}", v.dollar()),
            AmountNature::MoreThan(v) => format!(">{}", v.dollar()),
            AmountNature::MoreThanEqual(v) => format!(">={}", v.dollar()),
            AmountNature::LessThan(v) => format!("<{}", v.dollar()),
            AmountNature::LessThanEqual(v) => format!("<={}", v.dollar()),
        };
        write!(f, "{a}")
    }
}

#[derive(Copy, Clone)]
pub enum AmountType {
    Exact,
    MoreThan,
    MoreThanEqual,
    LessThan,
    LessThanEqual,
}

impl From<AmountType> for String {
    fn from(value: AmountType) -> Self {
        match value {
            AmountType::Exact => "exact".to_string(),
            AmountType::MoreThan => "more_than".to_string(),
            AmountType::MoreThanEqual => "more_than_equal".to_string(),
            AmountType::LessThan => "less_than".to_string(),
            AmountType::LessThanEqual => "less_than_equal".to_string(),
        }
    }
}

impl From<&str> for AmountType {
    fn from(value: &str) -> Self {
        match value {
            "exact" => AmountType::Exact,
            "more_than" => AmountType::MoreThan,
            "more_than_equal" => AmountType::MoreThanEqual,
            "less_than" => AmountType::LessThan,
            "less_than_equal" => AmountType::LessThanEqual,
            other => panic!("Invalid AmountType string: {other}"),
        }
    }
}

#[derive(Clone, Debug, Copy)]
pub enum ActivityNature {
    AddTx,
    EditTx,
    DeleteTx,
    SearchTx,
    PositionSwap,
}

impl From<&str> for ActivityNature {
    fn from(s: &str) -> Self {
        match s {
            "add_tx" => ActivityNature::AddTx,
            "edit_tx" => ActivityNature::EditTx,
            "delete_tx" => ActivityNature::DeleteTx,
            "search_tx" => ActivityNature::SearchTx,
            "position_swap" => ActivityNature::PositionSwap,
            other => panic!("Invalid TxType string: {other}"),
        }
    }
}

impl From<ActivityNature> for String {
    fn from(a: ActivityNature) -> Self {
        match a {
            ActivityNature::AddTx => "add_tx".to_string(),
            ActivityNature::EditTx => "edit_tx".to_string(),
            ActivityNature::DeleteTx => "delete_tx".to_string(),
            ActivityNature::SearchTx => "search_tx".to_string(),
            ActivityNature::PositionSwap => "position_swap".to_string(),
        }
    }
}

impl Display for ActivityNature {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ActivityNature::AddTx => write!(f, "Add Transaction"),
            ActivityNature::EditTx => write!(f, "Edit Transaction"),
            ActivityNature::DeleteTx => write!(f, "Delete Transaction"),
            ActivityNature::SearchTx => write!(f, "Search Transaction"),
            ActivityNature::PositionSwap => write!(f, "Position Swap"),
        }
    }
}

impl From<&str> for TxType {
    fn from(s: &str) -> Self {
        match s {
            "Income" => TxType::Income,
            "Expense" => TxType::Expense,
            "Transfer" => TxType::Transfer,
            "Borrow" => TxType::Borrow,
            "Lend" => TxType::Lend,
            "Borrow Repay" => TxType::BorrowRepay,
            "Lend Repay" => TxType::LendRepay,
            other => panic!("Invalid TxType string: {other}"),
        }
    }
}
