import csv
import hashlib
import json
import re
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
EDITION = "website-primer-2026-09-16"
DATA = ROOT / "data" / EDITION
ARCHIVE = ROOT / "backups" / EDITION
PUBLIC = ROOT / "website/public/downloads/primer"
REVIEWED = "2026-09-16"
FIELDS = [
    "hydro_gwh", "nuclear_gwh", "thermal_gwh", "wind_gwh", "solar_gwh",
    "generation_gwh", "pumping_gwh", "net_generation_gwh", "imports_gwh",
    "exports_gwh", "national_consumption_gwh", "losses_gwh",
    "final_consumption_gwh", "net_imports_gwh",
]


def write_json(path, data):
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n")


def artifact(path, download=None):
    content = path.read_bytes()
    result = {"path": str(path.relative_to(ROOT)), "sha256": hashlib.sha256(content).hexdigest(), "bytes": len(content)}
    if download:
        result["download"] = download
        target = ROOT / "website/public" / download.lstrip("/")
        target.parent.mkdir(parents=True, exist_ok=True)
        target.write_bytes(content)
    return result


def seasonal_data():
    text = (ROOT / "backups/2026-09-09/data/bfe/publications/12681.txt").read_text()
    table = text[text.index("Tab. 5"):text.index("Tab. 6")]
    rows = []
    season = "year"
    for line in table.splitlines():
        if line.strip() == "Winter – Hiver":
            season = "winter"
        if line.strip() == "Sommer – Eté":
            season = "summer"
        if not re.match(r"^\s*202[0-5]", line):
            continue
        cells = re.split(r"\s{2,}", line.strip())
        if len(cells) == 16:
            cells[-2:] = ["".join(cells[-2:])]
        assert len(cells) == 15, cells
        values = [int(re.sub(r"\s", "", cell).replace("–", "-")) for cell in cells[1:]]
        end_year = int(cells[0][-4:])
        start = f"{end_year}-04-01" if season == "summer" else f"{end_year - 1}-10-01"
        end = f"{end_year}-03-31" if season == "winter" else f"{end_year}-09-30"
        row = dict(period=cells[0], season=season, start=start, end=end, **dict(zip(FIELDS, values)))
        assert sum(row[field] for field in FIELDS[:5]) == row["generation_gwh"]
        assert row["generation_gwh"] - row["pumping_gwh"] == row["net_generation_gwh"]
        assert row["imports_gwh"] - row["exports_gwh"] == row["net_imports_gwh"]
        assert row["net_generation_gwh"] + row["net_imports_gwh"] == row["national_consumption_gwh"]
        assert row["national_consumption_gwh"] - row["losses_gwh"] == row["final_consumption_gwh"]
        rows.append(row)
    assert len(rows) == 15
    for annual in [row for row in rows if row["season"] == "year"]:
        parts = [row for row in rows if row["season"] != "year" and row["end"][:4] == annual["end"][:4]]
        assert len(parts) == 2
        for field in FIELDS:
            assert annual[field] == sum(row[field] for row in parts), (annual["period"], field)
    return rows


def main():
    DATA.mkdir(parents=True, exist_ok=True)
    catalog = json.loads((ROOT / "data/website-public-copy-2026-09-16/catalog.json").read_text())
    for source in catalog["sources"]:
        if source["id"] == "bfe-electricity-2025":
            source["locator"] += "; table 5, printed page 9: hydrological-year and seasonal balances; table 13, printed page 18: storage periods"
    definitions = [
        ("eia-electricity-units", "Measuring electricity", "U.S. Energy Information Administration (EIA)", "Electricity is measured in Watts and kilowatts; Electricity use over time is measured in Watthours", "Definitions of power and energy only. No US consumption figures are applied to Switzerland.", None),
        ("eia-electricity-storage", "Energy storage for electricity generation", "U.S. Energy Information Administration (EIA)", "Electricity generation capacity of energy storage systems; Pumped-storage hydroelectric systems", "Used for storage definitions and charging losses, not current prices or Swiss procurement capacity.", "2023-08-28"),
        ("swissenergy-heat-pumps", "Chauffez efficacement avec une pompe à chaleur", "SwissEnergy / Swiss Federal Office of Energy", "Comment fonctionne une pompe à chaleur ?; introductory explanation of environmental heat", "Used for the operating principle, not as a performance guarantee for a particular building or a national winter forecast.", None),
        ("eia-hydropower", "Hydropower explained", "U.S. Energy Information Administration (EIA)", "Hydropower relies on the water cycle; Hydropower facilities", "Used for the distinction between flowing water, reservoirs and pumped storage. No US fleet figures are applied to Switzerland.", None),
    ]
    for identifier, title, publisher, locator, note, date in definitions:
        metadata = json.loads((ARCHIVE / f"{identifier}.source.json").read_text())
        catalog["sources"].append(dict(
            id=identifier, title=title, publisher=publisher, kind="primary", url=metadata["url"],
            publicationDate=date, edition=f"Web edition retrieved {REVIEWED}", retrievedAt=metadata["retrieved_at"],
            reviewedAt=REVIEWED, locator=locator, note=note,
            artifacts=[artifact(ARCHIVE / f"{identifier}.html"), artifact(ARCHIVE / f"{identifier}.txt")],
        ))
    rows = seasonal_data()
    dataset = dict(
        title="Swiss seasonal electricity balances, 2020/21–2024/25", source="bfe-electricity-2025",
        locator="Table 5, printed page 9; table 13, printed page 18 for October–March and April–September periods",
        unit="GWh", reviewedAt=REVIEWED,
        method="Transcribed from the archived BFE text extraction. Every row balances; the two seasons sum to the hydrological year for all fields. TWh = GWh / 1000.",
        limitations="Physical cross-border flows, not contractual electricity origins or hourly adequacy. Positive net imports mean imports exceed exports. Generation includes pumped-storage output; pumping input is deducted separately. Hydro is not natural inflow alone. Years run October–September, not January–December. Seasonal PV comparisons also reflect the changing installed fleet.",
        rows=rows,
    )
    write_json(DATA / "seasonal-balances.json", dataset)
    with (DATA / "seasonal-balances.csv").open("w") as handle:
        writer = csv.DictWriter(handle, fieldnames=list(rows[0]))
        writer.writeheader()
        writer.writerows(rows)
    illustrations = dict(
        status="assumption", power_gw=1, usable_energy_gwh=4, constant_output_hours=4,
        formula="duration (h) = usable output energy (GWh) / constant power (GW) = 4 / 1",
        limitation="Teaching example, not an existing or proposed Swiss asset. Assumes a full store, constant output, no recharge and no further discharge losses or operating reserve. Charging energy and cost are not calculated.",
        units={"kwh_per_twh": 1000000000, "gwh_per_twh": 1000, "mwh_per_gwh": 1000, "mw_per_gw": 1000},
    )
    write_json(DATA / "illustrations.json", illustrations)
    concepts = [
        dict(id="power-and-energy", statement="Power is the rate of supplying or using electricity; energy is the amount over time. Watt-hour prefixes follow the corresponding watt prefixes.", sources=["eia-electricity-units"], boundary="Definitions, not measured appliance consumption. Unit conversions are exact."),
        dict(id="capacity-factor-not-uptime", statement="Capacity factor is actual energy divided by rated power times elapsed hours. Partial-load operation changes the result as well as outages.", sources=["nuclear-history-calculation"], boundary="Keep net/gross definitions and rating changes consistent; not simply the fraction of hours operating."),
        dict(id="seasonal-trade", statement="The 2024/25 hydrological year had net exports while its winter had net imports. Winter 2023/24 had net exports.", sources=["bfe-electricity-2025", "primer-calculations"], boundary="Physical cross-border flows. Does not establish unserved demand, unavoidable imports or hourly supply availability."),
        dict(id="winter-mix", statement="Hydro and nuclear were the two largest domestic generation categories in winter 2024/25. Solar generation was positive and lower than summer 2025 generation.", sources=["bfe-electricity-2025"], boundary="Table 5. Hydro includes pumped output before deducting pumping input. Seasonal solar comparison includes fleet growth."),
        dict(id="storage-shifts-energy", statement="Batteries and electricity-driven pumped storage must be charged and return less electricity than the charging cycle consumes.", sources=["eia-electricity-storage"], boundary="The statement concerns storage of electricity; natural water inflows can add energy to a mixed hydro scheme."),
        dict(id="hydro-types", statement="River hydro uses flowing water; reservoirs can retain natural inflows; pumped storage uses electricity to lift water for later generation.", sources=["eia-hydropower"], boundary="Physical principles, not evidence that all reservoirs can exchange water or have identical commercial and environmental constraints."),
        dict(id="heat-pump-principle", statement="Electric heat pumps move heat from air, ground or water; delivered heat includes environmental energy.", sources=["swissenergy-heat-pumps"], boundary="No fixed winter performance ratio is asserted. Performance depends on equipment and conditions."),
        dict(id="electrification-demand", statement="Replacing fuel-using equipment requires conversion efficiency and demand assumptions, not a one-for-one swap of fuel energy for electricity.", sources=["demand-model-v1"], boundary="Future national totals are scenarios. The guide does not claim a proven 2050 demand forecast."),
        dict(id="planning-tests", statement="Compare difficult-hour supply, construction and connection dates, dependencies, owner viability and adverse cases for all portfolios.", sources=[], boundary="Editorial assessment and proposed decision criteria, not a finding that one portfolio has already passed."),
    ]
    write_json(DATA / "concept-review.json", dict(reviewedAt=REVIEWED, concepts=concepts))
    catalog["sources"].append(dict(
        id="primer-calculations", title="Swiss seasonal balances and electricity examples", publisher="WattInfo",
        kind="project-calculation", url="/downloads/primer/seasonal-balances.json", publicationDate=REVIEWED,
        edition="Five hydrological years, 2020/21–2024/25", retrievedAt=REVIEWED, reviewedAt=REVIEWED,
        locator="Seasonal balance rows and definitions; separate illustrative storage calculation",
        note="Observed BFE seasonal data, exact unit conversions and an explicitly hypothetical storage example. Net imports do not measure unmet demand.",
        artifacts=[artifact(DATA / name, f"/downloads/primer/{name}") for name in ["seasonal-balances.json", "seasonal-balances.csv", "illustrations.json", "concept-review.json"]]
        + [artifact(Path(__file__), "/downloads/primer/build_website_primer.py")],
    ))

    def claim(identifier, label, value, period, definition, limitation, status="observed", unit="TWh", formula=None, sources=None):
        catalog["claims"].append(dict(
            id=identifier, label=label, value=value, unit=unit, status=status, period=period,
            definition=definition, limitation=limitation, formula=formula, reviewedAt=REVIEWED,
            sources=sources or ["bfe-electricity-2025", "primer-calculations"],
        ))

    for row in rows:
        if row["season"] != "winter":
            continue
        claim(f'winter-net-imports-{row["period"].replace("/", "-")}', "Winter net electricity imports", row["net_imports_gwh"] / 1000,
              f'{row["start"]} to {row["end"]}', "Physical imports minus physical exports. A negative value means net exports.",
              "A trade balance, not a measure of unmet demand. It does not show imports in each hour.")
    for season in ["summer", "year"]:
        row = next(row for row in rows if row["season"] == season and row["end"] == "2025-09-30")
        claim(f'{season}-net-exports-2025', f'{"Summer" if season == "summer" else "Hydrological-year"} net electricity exports', -row["net_imports_gwh"] / 1000,
              f'{row["start"]} to {row["end"]}', "Physical exports minus physical imports.",
              "The year runs October–September. A net export total does not mean there were no imports within the period.")
    winter = next(row for row in rows if row["season"] == "winter" and row["period"] == "2024/2025")
    for field, label in [("hydro", "Hydro"), ("nuclear", "Nuclear"), ("thermal", "Other thermal generation"), ("solar", "Solar"), ("wind", "Wind")]:
        claim(f'winter-{field}-2024-2025', f'{label}: winter electricity generation', winter[f"{field}_gwh"] / 1000,
              "2024-10-01 to 2025-03-31", "Domestic generation in BFE table 5, before deducting storage pumping input.",
              "Generation mix, not household electricity labels or electricity delivered to final consumers. Hydro includes pumped-storage output.")
    claim("illustrative-storage-hours", "Duration of the example store", 4, "Illustrative constant-output example",
          "Assumed usable energy of 4 GWh divided by assumed output of 1 GW.", illustrations["limitation"],
          status="calculated", unit="hours", formula=illustrations["formula"], sources=["eia-electricity-storage", "primer-calculations"])
    write_json(DATA / "catalog.json", catalog)
    write_json(ROOT / "website/src/data/catalog.json", catalog)
    write_json(ROOT / "website/public/downloads/catalog.json", catalog)
    print(f'Primer catalogue: {len(catalog["sources"])} sources, {len(catalog["claims"])} numeric claims; {len(rows)} reconciled seasonal rows.')


if __name__ == "__main__":
    main()
