from __future__ import annotations

from typing import Any

from .cache import get_or_fetch, read_cache
from .config import CACHE_TTL_DAYS, agency_epci_codes, load_scope
from .http_client import get_json

import os

BASE_URL = os.environ.get("API_GEO_BASE_URL", "https://geo.api.gouv.fr")
TTL_SECONDS = CACHE_TTL_DAYS * 24 * 3600

COMMUNE_FIELDS = "nom,code,population,codeDepartement,codeRegion,codeEpci,codesPostaux,centre"
EPCI_FIELDS = "nom,code,population,codesDepartements,codesRegions"


def fetch_epci(code: str) -> dict[str, Any]:
    return get_json(f"{BASE_URL}/epcis/{code}", params={"fields": EPCI_FIELDS})


def fetch_epci_communes(code: str) -> list[dict[str, Any]]:
    return get_json(
        f"{BASE_URL}/epcis/{code}/communes",
        params={"fields": COMMUNE_FIELDS, "format": "json"},
    )


def get_epci(code: str, *, force: bool = False) -> dict[str, Any]:
    item = get_or_fetch(
        f"geo_epci_{code}",
        TTL_SECONDS,
        lambda: fetch_epci(code),
        force=force,
        source=f"{BASE_URL}/epcis/{code}",
    )
    return item["payload"]


def get_epci_communes(code: str, *, force: bool = False) -> list[dict[str, Any]]:
    item = get_or_fetch(
        f"geo_epci_{code}_communes",
        TTL_SECONDS,
        lambda: fetch_epci_communes(code),
        force=force,
        source=f"{BASE_URL}/epcis/{code}/communes",
    )
    return item["payload"]


def cached_agency_communes() -> dict[str, Any] | None:
    item = read_cache("agency_communes")
    return item["payload"] if item else None


def build_agency_communes(*, force: bool = False) -> dict[str, Any]:
    scope = load_scope()
    epci_declared = {str(item.get("code")): item for item in scope.get("epcis", []) if item.get("code")}
    epcis: list[dict[str, Any]] = []
    communes: list[dict[str, Any]] = []
    seen_codes: set[str] = set()

    for code in agency_epci_codes():
        declared = epci_declared.get(code, {})
        epci = get_epci(code, force=force)
        epci = {**declared, **epci, "code": code}
        epcis.append(epci)
        for commune in get_epci_communes(code, force=force):
            ccode = str(commune.get("code"))
            if not ccode or ccode in seen_codes:
                continue
            seen_codes.add(ccode)
            commune["codeEpci"] = commune.get("codeEpci") or code
            commune["nomEpci"] = epci.get("nom") or epci.get("name")
            communes.append(commune)

    exclude = {str(code) for code in scope.get("exclude_communes", [])}
    communes = [c for c in communes if str(c.get("code")) not in exclude]

    result = {
        "scope": scope,
        "epcis": epcis,
        "communes": sorted(communes, key=lambda c: (c.get("nom") or "")),
        "counts": {
            "epcis": len(epcis),
            "communes": len(communes),
            "population_communes": sum(int(c.get("population") or 0) for c in communes),
        },
    }

    from .cache import write_cache

    write_cache("agency_communes", result, source=f"{BASE_URL}/epcis/[code]/communes")
    return result


def agency_scope(*, live: bool = False, force: bool = False) -> dict[str, Any]:
    cached = cached_agency_communes()
    if cached and not live and not force:
        return {**cached, "cache_status": "hit"}
    if not live and not force:
        scope = load_scope()
        return {
            "scope": scope,
            "epcis": scope.get("epcis", []),
            "communes": [],
            "counts": {"epcis": len(scope.get("epcis", [])), "communes": None, "population_communes": None},
            "cache_status": "missing",
            "message": "Cache des communes absent. Lancer scripts/refresh_external_cache.py --source geo ou appeler /api/agency/scope?live=true.",
        }
    return {**build_agency_communes(force=force), "cache_status": "refresh"}
