"""Currency conversion, so prices from different airports are comparable.

Rates are fetched once per collection run and cached. The fallback table exists
so a collection run still completes when the rate service is unreachable -- a
stale rate is far better than no comparison, and every price carries its
observation date anyway.
"""

import logging
from datetime import UTC, datetime

from app.services.collectors.fetch import fetch_json

logger = logging.getLogger(__name__)

RATES_URL = "https://open.er-api.com/v6/latest/USD"

# Units of the listed currency per 1 USD. Used only when the rate service is
# unreachable. AED is a hard peg; the rest are approximate and will drift.
FALLBACK_RATES: dict[str, float] = {
    "USD": 1.0,
    "AED": 3.6725,
    "EUR": 0.86,
    "GBP": 0.73,
    "CHF": 0.80,
    "CAD": 1.37,
    "SGD": 1.29,
    "JPY": 150.0,
    "THB": 33.0,
    "ISK": 137.0,
    "AUD": 1.53,
    "DKK": 6.40,
    "HKD": 7.8,
    "MXN": 18.5,
}


class FxRates:
    """Per-USD rates for one point in time."""

    def __init__(self, rates: dict[str, float], fetched_at: datetime, is_fallback: bool) -> None:
        self.rates = rates
        self.fetched_at = fetched_at
        self.is_fallback = is_fallback

    def to_usd(self, amount: float, currency: str) -> float | None:
        rate = self.rates.get((currency or "").upper())
        if not rate:
            return None
        return round(amount / rate, 2)


def load_rates() -> FxRates:
    now = datetime.now(UTC)
    try:
        payload = fetch_json(RATES_URL)
        rates = payload.get("rates") if isinstance(payload, dict) else None
        if isinstance(rates, dict) and rates.get("USD"):
            numeric = {k: float(v) for k, v in rates.items() if isinstance(v, int | float)}
            return FxRates(numeric, now, is_fallback=False)
        raise ValueError("rate payload missing rates")
    except Exception as exc:
        logger.warning("fx_rate_fetch_failed error=%s using_fallback=true", exc)
        return FxRates(dict(FALLBACK_RATES), now, is_fallback=True)
