"""Build the static-site data payload for web/.

Reads cache/addresses.parquet, slims to {addr, lat, lng}, writes web/data/
addresses.json. Copies the GeoJSONs produced by analyze.py into web/data/.
Generates web/data/manifest.json listing the available layers and scenarios.
"""
from __future__ import annotations
import json
import shutil
import sys
import time
from pathlib import Path

import geopandas as gpd

ROOT = Path(__file__).resolve().parent.parent
CACHE = ROOT / "cache"
OUT = ROOT / "out"
WEB_DATA = ROOT / "web" / "data"
WEB_DATA.mkdir(parents=True, exist_ok=True)


def build_addresses() -> None:
    t0 = time.time()
    print("loading addresses...", flush=True)
    addr = gpd.read_parquet(CACHE / "addresses.parquet").to_crs("EPSG:4326")
    addr = addr[addr.geometry.notna() & ~addr.geometry.is_empty].copy()
    addr["lng"] = addr.geometry.x.round(6)
    addr["lat"] = addr.geometry.y.round(6)
    addr["a"] = addr["FULLADDR"].fillna("").astype(str).str.strip()
    addr = addr[addr["a"] != ""].drop_duplicates(subset=["a", "lat", "lng"])

    # Slim shape: list of [addr, lat, lng] triples. Position-only for compact JSON.
    rows = addr[["a", "lat", "lng"]].values.tolist()
    out_path = WEB_DATA / "addresses.json"
    with open(out_path, "w") as f:
        json.dump(rows, f, separators=(",", ":"))
    print(f"  wrote {out_path.relative_to(ROOT)} "
          f"({len(rows):,} addresses, {out_path.stat().st_size/1e6:.2f} MB, "
          f"{time.time()-t0:.1f}s)")


def copy_geojsons() -> dict:
    """Copy out/*.geojson into web/data/. Return a manifest dict.

    First sweep stale eligible_*.geojson and marginal_/homes_protected_
    files from web/data/ that no longer exist in out/ -- otherwise a layer
    that used to be non-empty can keep serving its old polygons after a
    filter change made it empty.
    """
    sources = {p.name for p in OUT.glob("*.geojson")}
    stale_prefixes = ("eligible_", "marginal_parcels_", "homes_protected_")
    for existing in WEB_DATA.glob("*.geojson"):
        if existing.name.startswith(stale_prefixes) and existing.name not in sources:
            existing.unlink()
            print(f"  removed stale {existing.name}")

    layers = []
    scenarios = {}
    for gj in sorted(OUT.glob("*.geojson")):
        dst = WEB_DATA / gj.name
        shutil.copyfile(gj, dst)
        size = dst.stat().st_size
        layers.append({"file": gj.name, "size_kb": round(size / 1024, 1)})
        # Detect scenario suffix: eligible_200ft_buildable_scenarioFOO.geojson
        stem = gj.stem
        if "_" in stem:
            parts = stem.split("_")
            if len(parts) >= 4 and parts[0] == "eligible":
                ft = parts[1]  # e.g. "200ft"
                tier = parts[2]  # strict / buildable / siteable
                suffix = "_".join(parts[3:]) if len(parts) > 3 else "default"
                scenarios.setdefault(suffix, {}).setdefault(ft, {})[tier] = gj.name
    return {"layers": layers, "scenarios": scenarios}


def write_manifest(manifest: dict) -> None:
    # Headline numbers parsed from CSVs so the page can show stats without
    # the user having to open files.
    headlines = {}
    for ft in ("200ft", "500ft", "1000ft"):
        for tier in ("strict", "buildable"):
            f = OUT / f"eligible_{ft}_{tier}.csv"
            if f.exists():
                # rows = total lines - header
                n = max(0, sum(1 for _ in open(f)) - 1)
                headlines[f"{ft}_{tier}"] = n
    manifest["headlines"] = headlines
    manifest["generated_at"] = time.strftime("%Y-%m-%d %H:%M:%S")
    out = WEB_DATA / "manifest.json"
    with open(out, "w") as f:
        json.dump(manifest, f, indent=2)
    print(f"  wrote {out.relative_to(ROOT)}")


def main() -> int:
    build_addresses()
    print("copying geojsons...")
    manifest = copy_geojsons()
    for layer in manifest["layers"]:
        print(f"  {layer['file']:48s}  {layer['size_kb']:>8.1f} KB")
    write_manifest(manifest)
    return 0


if __name__ == "__main__":
    sys.exit(main())
