"""Sub-agente: Demografía.

Fuente: Census ACS API (key gratuita en api.census.gov/data/key_signup.html).
Sin key, usa datos estimados por ZIP desde datos públicos del Census.
"""

from __future__ import annotations

import os
import re
import time
import requests
from data_fetchers.base import USER_AGENT, DEFAULT_TIMEOUT

CENSUS_KEY = os.getenv("CENSUS_API_KEY", "")
ACS_BASE = "https://api.census.gov/data/2022/acs/acs5"

# Variables ACS5 a consultar
ACS_VARS = {
    "B19013_001E": "median_household_income",
    "B01002_001E": "median_age",
    "B23025_005E": "unemployed",
    "B23025_002E": "labor_force",
    "B15003_022E": "bachelors_degree",
    "B15003_001E": "population_25plus",
    "B03002_003E": "white_non_hispanic",
    "B03002_004E": "black",
    "B03002_012E": "hispanic",
    "B03002_006E": "asian",
    "B01003_001E": "total_population",
}


def run(lat: float, lon: float, address: str, tract_geoid: str = "",
        state_fips: str = "", county_fips: str = "") -> dict:
    result = {
        "median_household_income": None,
        "median_age": None,
        "unemployment_rate": None,
        "education_bachelors_pct": None,
        "ethnicity": {},
        "total_population": None,
        "sources": [],
        "errors": [],
    }

    # Extraer ZIP para fallback
    zip_m = re.search(r"\b(\d{5})\b", address)
    zip_code = zip_m.group(1) if zip_m else ""

    # --- Census ACS API ---
    if CENSUS_KEY and state_fips and county_fips:
        try:
            acs = _census_acs(state_fips, county_fips, tract_geoid)
            result.update(acs)
            result["sources"].append("Census ACS 5-Year")
        except Exception as e:
            result["errors"].append(f"Census ACS: {e}")
    else:
        result["errors"].append(
            "Census API key no configurada. Agregar CENSUS_API_KEY en .env. "
            "Obtener en: https://api.census.gov/data/key_signup.html"
        )

    # --- Fallback: Census ZIP (sin key) ---
    if not result["sources"] and zip_code:
        try:
            z = _census_zip_no_key(zip_code)
            result.update(z)
            result["sources"].append("Census ZIP (estimado)")
        except Exception as e:
            result["errors"].append(f"Census ZIP fallback: {e}")

    return result


def _census_acs(state_fips: str, county_fips: str, tract_geoid: str) -> dict:
    """Consulta Census ACS 5-Year para un census tract."""
    vars_str = ",".join(ACS_VARS.keys())

    # Extraer county y tract de GEOID (12 dígitos: SS+CCC+TTTTTT)
    county = county_fips[-3:] if len(county_fips) >= 3 else county_fips
    state = state_fips[:2]
    tract = tract_geoid[-6:] if len(tract_geoid) >= 6 else "*"

    params = {
        "get": vars_str,
        "for": f"tract:{tract}",
        "in": f"state:{state} county:{county}",
        "key": CENSUS_KEY,
    }
    headers = {"User-Agent": USER_AGENT}
    time.sleep(0.5)
    r = requests.get(ACS_BASE, params=params, headers=headers, timeout=DEFAULT_TIMEOUT)
    r.raise_for_status()
    rows = r.json()
    if len(rows) < 2:
        return {}

    header = rows[0]
    vals = rows[1]
    data = {ACS_VARS.get(h, h): _safe_int(v) for h, v in zip(header, vals) if h in ACS_VARS}

    return _process_acs(data)


def _census_zip_no_key(zip_code: str) -> dict:
    """Census sin key — datos por ZIP usando endpoint público."""
    vars_str = "B19013_001E,B01002_001E,B01003_001E"
    params = {"get": vars_str, "for": f"zip code tabulation area:{zip_code}"}
    r = requests.get(ACS_BASE, params=params,
                     headers={"User-Agent": USER_AGENT}, timeout=DEFAULT_TIMEOUT)
    r.raise_for_status()
    rows = r.json()
    if len(rows) < 2:
        return {}
    header = rows[0]
    vals = rows[1]
    raw = dict(zip(header, vals))
    return {
        "median_household_income": _safe_int(raw.get("B19013_001E")),
        "median_age": _safe_int(raw.get("B01002_001E")),
        "total_population": _safe_int(raw.get("B01003_001E")),
    }


def _process_acs(data: dict) -> dict:
    total_pop = data.get("total_population") or 1
    labor = data.get("labor_force") or 1
    pop25 = data.get("population_25plus") or 1

    unemployed = data.get("unemployed") or 0
    bachelors = data.get("bachelors_degree") or 0
    white = data.get("white_non_hispanic") or 0
    black = data.get("black") or 0
    hispanic = data.get("hispanic") or 0
    asian = data.get("asian") or 0

    ethnicity = {
        "white_non_hispanic_pct": round(white / total_pop * 100, 1),
        "black_pct": round(black / total_pop * 100, 1),
        "hispanic_pct": round(hispanic / total_pop * 100, 1),
        "asian_pct": round(asian / total_pop * 100, 1),
        "other_pct": max(0, round((total_pop - white - black - hispanic - asian) / total_pop * 100, 1)),
    }

    return {
        "median_household_income": data.get("median_household_income"),
        "median_age": data.get("median_age"),
        "unemployment_rate": round(unemployed / labor * 100, 1) if labor > 0 else None,
        "education_bachelors_pct": round(bachelors / pop25 * 100, 1) if pop25 > 0 else None,
        "ethnicity": ethnicity,
        "total_population": total_pop,
    }


def _safe_int(v) -> int | None:
    try:
        return int(v)
    except (TypeError, ValueError):
        return None


def score(data: dict) -> int:
    """Score 0-100 basado en indicadores socioeconómicos."""
    s = 50

    income = data.get("median_household_income")
    if income is not None:
        if income >= 100000:
            s += 20
        elif income >= 75000:
            s += 12
        elif income >= 50000:
            s += 5
        elif income >= 35000:
            s -= 5
        else:
            s -= 15

    unemp = data.get("unemployment_rate")
    if unemp is not None:
        if unemp <= 3:
            s += 15
        elif unemp <= 5:
            s += 8
        elif unemp <= 8:
            s += 0
        elif unemp <= 12:
            s -= 8
        else:
            s -= 15

    edu = data.get("education_bachelors_pct")
    if edu is not None:
        if edu >= 50:
            s += 10
        elif edu >= 35:
            s += 5
        elif edu >= 20:
            s += 0
        else:
            s -= 5

    return min(100, max(0, s))
