"""
Financial Modeling Prep calendar service — upcoming events with consensus forecasts.
Free API key: https://financialmodelingprep.com/developer/docs (250 req/day free)
Endpoint: GET https://financialmodelingprep.com/api/v3/economic_calendar
"""
import logging
from datetime import datetime, timezone, timedelta, date
from typing import Any, Dict

import httpx

logger = logging.getLogger(__name__)

_FMP_BASE = "https://financialmodelingprep.com/api/v3"

# FMP country codes → our currency codes
_FMP_COUNTRY_TO_CCY: Dict[str, str] = {
    "US": "USD", "EU": "EUR", "GB": "GBP", "JP": "JPY",
    "AU": "AUD", "CA": "CAD", "NZ": "NZD", "CH": "CHF", "CN": "CNY",
}

# FMP impact strings → our normalized labels
_FMP_IMPACT: Dict[str, str] = {
    "High":   "high",
    "Medium": "medium",
    "Low":    "low",
}


def _get_fmp_key() -> str:
    from services.database import get_config
    return (get_config("fmp_api_key") or "").strip()


def check_fmp_key() -> Dict[str, Any]:
    key = _get_fmp_key()
    return {
        "configured": bool(key),
        "preview": (key[:4] + "…" + key[-4:]) if len(key) > 8 else ("***" if key else ""),
    }


def _parse_fmp_dt(dt_str: str) -> tuple[str, str]:
    """Parse FMP datetime '2026-07-04 12:30:00' (UTC) → (date, time)."""
    if not dt_str:
        return "", "00:00"
    try:
        # FMP returns UTC strings like "2026-07-04 12:30:00" or ISO format
        dt_str = dt_str.replace("T", " ").split(".")[0]
        dt = datetime.strptime(dt_str, "%Y-%m-%d %H:%M:%S")
        return dt.strftime("%Y-%m-%d"), dt.strftime("%H:%M")
    except Exception:
        return dt_str[:10] if len(dt_str) >= 10 else "", "00:00"


def fetch_upcoming(weeks_ahead: int = 6) -> Dict[str, Any]:
    """
    Fetch upcoming calendar events from FMP API and upsert into ff_calendar.
    One request covers all countries and the full date range.
    """
    from services.database import get_conn
    from services.ff_calendar import _upsert_batch, FF_TO_FRED

    key = _get_fmp_key()
    if not key:
        return {
            "error": (
                "FMP API key not configured. "
                "Register free at financialmodelingprep.com/developer/docs"
            )
        }

    today = date.today()
    date_to = today + timedelta(weeks=weeks_ahead)

    url = f"{_FMP_BASE}/economic_calendar"
    params = {
        "from":   str(today),
        "to":     str(date_to),
        "apikey": key,
    }

    print(f"[FMP calendar] fetching {today} -> {date_to}", flush=True)

    try:
        resp = httpx.get(url, params=params, timeout=30, follow_redirects=True)
        print(f"[FMP calendar] HTTP {resp.status_code}", flush=True)

        if resp.status_code == 401:
            return {"error": "Invalid FMP API key"}
        if resp.status_code == 403:
            return {"error": "Economic Calendar not available on FMP free plan — requires Starter ($14.99/month) at financialmodelingprep.com/developer/docs"}

        resp.raise_for_status()
        events = resp.json()

        # FMP returns {"Error Message": "..."} on bad key
        if isinstance(events, dict) and "Error Message" in events:
            return {"error": events["Error Message"]}

        if not isinstance(events, list):
            return {"error": f"Unexpected FMP response: {str(events)[:200]}"}

    except Exception as e:
        return {"error": f"FMP request failed: {e}"}

    batch = []
    for ev in events:
        country_code = (ev.get("country") or "").upper().strip()
        ccy = _FMP_COUNTRY_TO_CCY.get(country_code)
        if not ccy:
            continue

        event_name = (ev.get("event") or "").strip()
        if not event_name:
            continue

        impact_raw = (ev.get("impact") or "").strip()
        impact = _FMP_IMPACT.get(impact_raw, "low")

        dt_str = (ev.get("date") or "").strip()
        ev_date, ev_time = _parse_fmp_dt(dt_str)
        if not ev_date:
            continue

        # FMP uses "actual", "previous", "estimate" (= consensus forecast)
        actual   = _fmt(ev.get("actual"))
        forecast = _fmt(ev.get("estimate"))    # FMP calls it "estimate"
        previous = _fmt(ev.get("previous"))

        series_id = FF_TO_FRED.get(event_name)

        batch.append((
            ev_date, ev_time, ccy, impact, event_name,
            actual or None, forecast or None, previous or None,
            series_id, None, "fmp_api",
        ))

    conn = get_conn()
    if batch:
        _upsert_batch(conn, batch)
        conn.commit()
    conn.close()

    print(f"[FMP calendar] {len(batch)} events upserted", flush=True)
    return {
        "total_upserted": len(batch),
        "date_from":      str(today),
        "date_to":        str(date_to),
    }


def _fmt(v: Any) -> str:
    if v is None:
        return ""
    try:
        f = float(v)
        if f != f:   # NaN
            return ""
        return str(int(f)) if f == int(f) else str(round(f, 4))
    except (TypeError, ValueError):
        return str(v).strip()
