"""CBS Open Data — gemeente demografie via OData."""
from __future__ import annotations

import json
import re
import urllib.parse
import urllib.request
from typing import Any, Optional

CBS_BASE = "https://opendata.cbs.nl/ODataApi/OData"
_GEMEENTE_CACHE: dict[str, dict[str, Any]] = {}


def _int_val(raw: Any) -> Optional[int]:
    if raw is None:
        return None
    s = str(raw).strip().replace(".", "")
    if not s or s == ".":
        return None
    try:
        return int(s)
    except ValueError:
        return None


def _float_val(raw: Any) -> Optional[float]:
    if raw is None:
        return None
    s = str(raw).strip()
    if not s or s == ".":
        return None
    try:
        return float(s)
    except ValueError:
        return None


def _fetch_untyped(dataset: str, filter_expr: str, top: int = 1) -> list[dict[str, Any]]:
    params = urllib.parse.urlencode(
        {"$filter": filter_expr, "$top": str(top), "$format": "json"},
        quote_via=urllib.parse.quote,
    )
    url = f"{CBS_BASE}/{dataset}/UntypedDataSet?{params}"
    with urllib.request.urlopen(url, timeout=45) as resp:
        data = json.loads(resp.read().decode())
    return data.get("value", [])


def _normalize_gm(code: str) -> str:
    code = (code or "").strip().upper()
    if code.startswith("GM"):
        return code
    digits = re.sub(r"\D", "", code)
    return f"GM{digits}" if digits else code


def fetch_gemeente_stats(gemeente_code: str) -> Optional[dict[str, Any]]:
    gm = _normalize_gm(gemeente_code)
    if not gm:
        return None
    if gm in _GEMEENTE_CACHE:
        return _GEMEENTE_CACHE[gm]

    pop_rows = _fetch_untyped(
        "03759ned",
        f"RegioS eq '{gm}' and Leeftijd eq '10000' and Geslacht eq 'T001038' "
        f"and BurgerlijkeStaat eq 'T001019' and substringof('2024',Perioden)",
    )
    income_rows = _fetch_untyped(
        "86005NED",
        f"RegioS eq '{gm}' and substringof('2023',Perioden) and Geslacht eq 'T001038'",
    )
    area_rows = _fetch_untyped(
        "84583NED",
        f"startswith(WijkenEnBuurten,'{gm}') and SoortRegio_2 eq 'Gemeente  '",
    )

    population = _int_val(pop_rows[0].get("BevolkingOp1Januari_1")) if pop_rows else None
    avg_income = None
    median_income = None
    if income_rows:
        avg_income = _float_val(income_rows[0].get("GemiddeldPersoonlijkInkomen_6"))
        median_income = _float_val(income_rows[0].get("MediaanPersoonlijkInkomen_7"))
        if avg_income:
            avg_income *= 1000
        if median_income:
            median_income *= 1000

    area = area_rows[0] if area_rows else {}
    pop_area = _int_val(area.get("AantalInwoners_5")) or population
    households = _int_val(area.get("HuishoudensTotaal_28"))
    niet_westers = _int_val(area.get("NietWestersTotaal_18"))
    marokko = _int_val(area.get("Marokko_19"))
    turkije = _int_val(area.get("Turkije_22"))
    suriname = _int_val(area.get("Suriname_21"))
    avg_hh_size = _float_val(area.get("GemiddeldeHuishoudensgrootte_32"))
    income_per_inhabitant = _float_val(area.get("GemiddeldInkomenPerInwoner_72"))
    if income_per_inhabitant and not avg_income:
        avg_income = income_per_inhabitant * 1000

    muslim_proxy_pct = None
    niet_westers_pct = None
    if pop_area and pop_area > 0:
        if marokko is not None and turkije is not None:
            muslim_proxy_pct = round((marokko + turkije) / pop_area * 100, 2)
        if niet_westers is not None:
            niet_westers_pct = round(niet_westers / pop_area * 100, 2)

    stats = {
        "gemeente_code": gm,
        "population": pop_area,
        "households": households,
        "avg_household_size": avg_hh_size,
        "avg_income": avg_income,
        "median_income": median_income,
        "unemployment_rate": None,
        "ethnic_composition": {
            "niet_westers_totaal": niet_westers,
            "niet_westers_pct": niet_westers_pct,
            "marokko": marokko,
            "turkije": turkije,
            "suriname": suriname,
        },
        "religious_composition": {
            "muslim_proxy_pct": muslim_proxy_pct,
            "note": "Indicatief: Marokko+Turkije / bevolking (CBS Kerncijfers wijken en buurten)",
        },
        "education_level": {
            "laag": _int_val(area.get("OpleidingsniveauLaag_64")),
            "middelbaar": _int_val(area.get("OpleidingsniveauMiddelbaar_65")),
            "hoog": _int_val(area.get("OpleidingsniveauHoog_66")),
        },
        "housing_type": {
            "koop_pct": _float_val(area.get("Koopwoningen_40")),
            "huur_pct": _float_val(area.get("HuurwoningenTotaal_41")),
        },
        "car_ownership": _float_val(area.get("PersonenautoSPerHuishouden_102")),
        "data_granularity": "gemeente",
        "data_source": "cbs+pdok",
    }
    _GEMEENTE_CACHE[gm] = stats
    return stats
