use crate::types::CalcResult;
use chrono::NaiveDate;
use std::collections::HashMap;

/// Evaluation context holding exchange rates, current date, variables, and other state
/// needed during interpretation.
pub struct EvalContext {
    /// Exchange rates relative to USD. Key is currency code (e.g. "EUR"),
    /// value is how many units of that currency per 1 USD.
    pub exchange_rates: HashMap<String, f64>,
    /// The current date for resolving `today`.
    pub today: NaiveDate,
    /// Named variables set via assignment expressions (e.g., `x = 5`).
    pub variables: HashMap<String, CalcResult>,
}

impl EvalContext {
    pub fn new() -> Self {
        Self {
            exchange_rates: HashMap::new(),
            today: chrono::Local::now().date_naive(),
            variables: HashMap::new(),
        }
    }

    /// Create a context with a fixed date (useful for testing).
    pub fn with_date(today: NaiveDate) -> Self {
        Self {
            exchange_rates: HashMap::new(),
            today,
            variables: HashMap::new(),
        }
    }

    /// Set exchange rate: 1 USD = `rate` units of `currency`.
    pub fn set_rate(&mut self, currency: &str, rate: f64) {
        self.exchange_rates.insert(currency.to_string(), rate);
    }

    /// Store a variable result.
    pub fn set_variable(&mut self, name: &str, result: CalcResult) {
        self.variables.insert(name.to_string(), result);
    }

    /// Retrieve a variable result.
    pub fn get_variable(&self, name: &str) -> Option<&CalcResult> {
        self.variables.get(name)
    }

    /// Convert `amount` from `from_currency` to `to_currency`.
    /// Returns None if either rate is missing.
    pub fn convert_currency(
        &self,
        amount: f64,
        from_currency: &str,
        to_currency: &str,
    ) -> Option<f64> {
        let from_rate = if from_currency == "USD" {
            1.0
        } else {
            *self.exchange_rates.get(from_currency)?
        };
        let to_rate = if to_currency == "USD" {
            1.0
        } else {
            *self.exchange_rates.get(to_currency)?
        };
        let usd_amount = amount / from_rate;
        Some(usd_amount * to_rate)
    }
}

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