use std::time::Duration;

use serde::{Deserialize, Serialize};

use crate::{
    bank::Bank,
    cycle::Cycle,
    envelope::{current_time, euros_on_track, percentage_between, Envelope},
    euro::Euros,
    frontend::Color,
    EnvelopeId,
};

#[derive(Clone, Default, Debug, Deserialize, Serialize)]
pub enum SavingGoal {
    #[default]
    None,
    Amount(Euros),
    SavingGoal {
        start: Duration,
        end: Duration,
        amount: Euros,
    },
    Spending {
        cycle: Cycle,
        amount: Euros,
    },
    SavingBuilding {
        amount: Euros,
    },
}

impl SavingGoal {
    pub fn color_status(&self, bank: &Bank, envelope: EnvelopeId) -> Color {
        let assigned = bank.assigned(envelope);
        match self {
            SavingGoal::None => Color::Green,
            SavingGoal::Amount(val) => {
                if val.total_cents() <= assigned.total_cents() {
                    Color::Green
                } else {
                    Color::Yellow
                }
            }
            SavingGoal::SavingGoal { start, end, amount } => {
                let now = current_time();
                if now > *end {
                    if assigned.total_cents() >= amount.total_cents() {
                        Color::Green
                    } else {
                        Color::Red
                    }
                } else {
                    let track = euros_on_track(now, *start, *end, *amount);
                    if track.total_cents() > assigned.total_cents() {
                        Color::Yellow
                    } else {
                        Color::Green
                    }
                }
            }
            SavingGoal::Spending { cycle, amount } => {
                if assigned.total_cents() < 0 {
                    return Color::Red;
                }

                let spent = bank.money_spent_in_timeframe(envelope, cycle.start(), cycle.end());

                if assigned.total_cents() + spent.total_cents() >= amount.total_cents() {
                    Color::Green
                } else {
                    Color::Red
                }
            }
            SavingGoal::SavingBuilding { amount } => {
                let assigned =
                    bank.assigned_in_timeframe(envelope, Cycle::Week.start(), Cycle::Week.end());
                if assigned.total_cents() < amount.total_cents() {
                    Color::Yellow
                } else {
                    Color::Green
                }
            }
        }
    }

    pub fn is_none(&self) -> bool {
        matches!(self, Self::None)
    }

    pub fn display(&self, bank: &Bank, envelope: &Envelope) -> String {
        let assigned = bank.assigned(envelope.id());
        match self {
            SavingGoal::None => assigned.print(),
            SavingGoal::Amount(eur) => {
                format!("{}/{}", assigned.print(), eur.print())
            }
            SavingGoal::SavingGoal { start, end, amount } => {
                let percentage = percentage_between(current_time(), *start, *end);
                let final_goal = *amount;
                let current_track: Euros = ((f32::from(final_goal)) * percentage).into();

                format!("{}/{}", assigned.print(), current_track.print())
            }
            SavingGoal::Spending { cycle, amount } => {
                let start = cycle.start();
                let end = cycle.end();

                let moneystuff = bank.money_spent_in_timeframe(envelope.id(), start, end);
                let assigned = assigned + moneystuff;
                let track = euros_on_track(current_time(), start, end, *amount);

                let cycles_paid = f32::from(assigned) / f32::from(*amount);

                let s = format!(
                    "track: {}, spent: {}, total assigned: {}, goal: {}, paid cycles: {}",
                    track.print(),
                    moneystuff.print(),
                    assigned.print(),
                    amount.print(),
                    cycles_paid
                );
                s
            }
            SavingGoal::SavingBuilding { amount } => {
                let assigned_in_cycle = bank.assigned_in_timeframe(
                    envelope.id(),
                    Cycle::Week.start(),
                    Cycle::Week.end(),
                );

                format!("{}/{}", assigned_in_cycle.print(), amount.print())
            }
        }
    }
}