from __future__ import annotations

import argparse
import csv
import hashlib
import json
from pathlib import Path


ROOT = Path(__file__).resolve().parent
EXPECTED = {
    "energy-monthly-generation.csv": "1fefcace69af9fefcda01047aeb0b61109d65b5a51cc3d220ebbf9da90d20359",
    "taipower-generation-purchases.csv": "3dd42fc6eb3955548f36f881fea1580d573eedefaa186b0a41b11b828af04dde",
    "taipower-emission-factors.csv": "acbfd597dd8b27dad9ffb186086a1e818356e71edfb742191ab6dfce604e1fc9",
    "2025-electricity-emission-factor.pdf": "4a4fa4646c53ef8602878dd65e4879fc72313ded4e345f80644c3cb723b09f1c",
}

MONTHLY_COLUMNS = {
    "日期(年/月)": "month",
    "全國發電量_總計(數值)": "total_million_kwh",
    "全國發電量_抽蓄水力(數值)": "pumped_storage_million_kwh",
    "全國發電量_火力_合計(數值)": "thermal_total_million_kwh",
    "全國發電量_火力_燃煤(數值)": "coal_million_kwh",
    "全國發電量_火力_燃油(數值)": "oil_million_kwh",
    "全國發電量_火力_燃氣(數值)": "gas_million_kwh",
    "全國發電量_核能(數值)": "nuclear_million_kwh",
    "全國發電量_再生能源_合計(數值)": "renewable_total_million_kwh",
    "全國發電量_再生能源_慣常水力(數值)": "conventional_hydro_million_kwh",
    "全國發電量_再生能源_地熱(數值)": "geothermal_million_kwh",
    "全國發電量_再生能源_太陽光電(數值)": "solar_million_kwh",
    "全國發電量_再生能源_風力(數值)": "wind_million_kwh",
    "全國發電量_再生能源_生質能(數值)": "biomass_million_kwh",
    "全國發電量_再生能源_廢棄物(數值)": "waste_million_kwh",
}

ENERGY_TYPE_MAP = {
    "抽蓄水力": "pumped_storage",
    "燃油": "oil",
    "燃煤": "coal",
    "燃氣": "gas",
    "核能": "nuclear",
    "地熱": "geothermal",
    "慣常水力": "conventional_hydro",
    "風力": "wind",
    "太陽能": "solar",
    "電池": "battery",
    "承攬水力": "contracted_hydro",
    "水力": "hydro",
    "生質能": "biomass",
    "廢棄物": "waste",
    "垃圾沼氣": "waste_biogas",
    "汽電共生": "cogeneration",
}


def sha256(path: Path) -> str:
    digest = hashlib.sha256()
    with path.open("rb") as stream:
        for chunk in iter(lambda: stream.read(1024 * 1024), b""):
            digest.update(chunk)
    return digest.hexdigest()


def require_sources(source_cache: Path) -> dict[str, dict[str, object]]:
    checks: dict[str, dict[str, object]] = {}
    for name, expected in EXPECTED.items():
        path = source_cache / name
        if not path.is_file():
            raise FileNotFoundError(f"Missing source file: {path}")
        actual = sha256(path)
        if actual != expected:
            raise ValueError(f"Source hash changed for {name}: {actual}")
        checks[name] = {"bytes": path.stat().st_size, "sha256": actual}
    return checks


def write_monthly_subset(source_cache: Path, data_dir: Path) -> int:
    source = source_cache / "energy-monthly-generation.csv"
    with source.open("r", encoding="utf-8-sig", newline="") as stream:
        reader = csv.DictReader(stream)
        missing = [name for name in MONTHLY_COLUMNS if name not in (reader.fieldnames or [])]
        if missing:
            raise ValueError(f"Monthly source is missing fields: {missing}")
        rows = []
        for raw in reader:
            month = raw["日期(年/月)"].strip()
            if not ("201601" <= month <= "202512"):
                continue
            cleaned = {MONTHLY_COLUMNS["日期(年/月)"]: month}
            for source_name, public_name in list(MONTHLY_COLUMNS.items())[1:]:
                value = raw[source_name].strip()
                if value == "":
                    raise ValueError(f"Missing value in {source_name} for {month}")
                cleaned[public_name] = f"{float(value):.9f}".rstrip("0").rstrip(".")
            rows.append(cleaned)
    if len(rows) != 120:
        raise ValueError(f"Expected 120 complete months for 2016-2025; found {len(rows)}")
    target = data_dir / "energy-monthly-generation-2016-2025.csv"
    with target.open("w", encoding="utf-8", newline="") as stream:
        writer = csv.DictWriter(stream, fieldnames=list(MONTHLY_COLUMNS.values()))
        writer.writeheader()
        writer.writerows(rows)
    return len(rows)


def write_taipower_subset(source_cache: Path, data_dir: Path) -> int:
    source = source_cache / "taipower-generation-purchases.csv"
    rows = []
    with source.open("r", encoding="cp950", newline="") as stream:
        reader = csv.reader(stream)
        header = next(reader)
        if header != ["年度", "發購電", "能源別", "發購電量"]:
            raise ValueError(f"Unexpected Taipower header: {header}")
        for year, supply_scope, energy_type, value in reader:
            if not (2016 <= int(year) <= 2025):
                continue
            if energy_type not in ENERGY_TYPE_MAP:
                raise ValueError(f"Unmapped Taipower energy type: {energy_type}")
            rows.append(
                {
                    "year": year,
                    "supply_scope": "taipower_owned" if supply_scope == "台電" else "purchased",
                    "energy_type": ENERGY_TYPE_MAP[energy_type],
                    "net_generation_or_purchase_kwh": value.strip(),
                }
            )
    if not rows or {row["year"] for row in rows} != {str(year) for year in range(2016, 2026)}:
        raise ValueError("Taipower subset does not cover every year from 2016 through 2025")
    target = data_dir / "taipower-net-generation-purchases-2016-2025.csv"
    with target.open("w", encoding="utf-8", newline="") as stream:
        writer = csv.DictWriter(stream, fieldnames=list(rows[0]))
        writer.writeheader()
        writer.writerows(rows)
    return len(rows)


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--source-cache", type=Path, required=True)
    args = parser.parse_args()
    data_dir = ROOT / "data"
    data_dir.mkdir(parents=True, exist_ok=True)
    checks = require_sources(args.source_cache.resolve())
    monthly_rows = write_monthly_subset(args.source_cache.resolve(), data_dir)
    taipower_rows = write_taipower_subset(args.source_cache.resolve(), data_dir)
    checks_payload = {
        "retrievedAt": "2026-08-03T10:53:00+08:00",
        "sourceFiles": checks,
        "derivedRows": {"monthlyGeneration": monthly_rows, "taipowerGenerationPurchases": taipower_rows},
        "note": "Raw source files remain in the private run staging cache and are not included in the public package. The public CSV files are limited, normalized subsets under OGDL 1.0 with attribution."
    }
    (data_dir / "source-file-checksums.json").write_text(
        json.dumps(checks_payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8"
    )
    print(json.dumps({"monthlyRows": monthly_rows, "taipowerRows": taipower_rows}, ensure_ascii=False))


if __name__ == "__main__":
    main()
