#!/usr/bin/env python3
"""
Fáze 3 (teaser) – prototyp čistého jádra plánovače nad golden fixtures.

Vezme STEJNÉ vstupy jako produkční solver (golden fixtures: forecast PV, baseline
load, efektivní ceny, battery/grid context) a vyřeší je ČISTÝM ekonomickým MILP
(scripts/harness/economics_report.solve_oracle): cash + degradace + terminal SoC,
tvrdá pravidla (block_export_on_negative_sell, curtail jen pole A, výkonové stropy),
ŽÁDNÉ heuristické penalty.

Porovná s výsledkem současného plánovače (golden snapshoty):
  - cashflow current vs clean na identických vstupech (modelované Kč),
  - feasibility (extrémní den 2026-05-01 je pro současný plánovač Infeasible).

Není to produkční náhrada (chybí EV deadline, TČ/TUV, provozní režimy) — je to
měření, kolik ekonomiky stojí heuristická vrstva. Spouštět z backend/:
  python3 ../scripts/harness/clean_core_prototype.py
"""

from __future__ import annotations

import importlib.util
import json
import sys
from datetime import datetime
from pathlib import Path

HARNESS = Path(__file__).resolve().parent
BACKEND = HARNESS.parents[1] / "backend"
sys.path.insert(0, str(BACKEND))

_spec = importlib.util.spec_from_file_location("econ", HARNESS / "economics_report.py")
econ = importlib.util.module_from_spec(_spec)
sys.modules["econ"] = econ  # dataclasses vyžadují modul v sys.modules
_spec.loader.exec_module(econ)

FIXTURES = sorted((BACKEND / "tests" / "golden" / "fixtures").glob("*.json"))
SNAPSHOTS = BACKEND / "tests" / "golden" / "snapshots"


def _fixture_to_inputs(fx: dict):
    ctx = fx["context_json"]
    b = ctx["battery"]
    bat = econ.BatteryParams(
        usable_wh=float(b["usable_capacity_wh"]),
        min_soc_wh=float(b["min_soc_wh"]),
        soc_max_wh=float(b.get("planner_soc_max_wh", b["soc_max_wh"])),
        charge_eff=float(b["charge_efficiency"]),
        discharge_eff=float(b["discharge_efficiency"]),
        max_charge_w=float(b["max_charge_power_w"]),
        max_discharge_w=float(b["max_discharge_power_w"]),
        degradation_czk_kwh=float(b["degradation_cost_czk_kwh"]),
    )
    g = ctx["grid"]
    grid = {
        "max_import_w": float(g["max_import_power_w"]),
        "max_export_w": float(g["max_export_power_w"]),
        "block_export_on_negative_sell": bool(g.get("block_export_on_negative_sell") or False),
    }
    slots = []
    for r in fx["slot_rows"]:
        # forecast (W) → energie slotu (Wh): ×0.25 h
        slots.append(
            econ.DaySlot(
                interval_start=datetime.fromisoformat(r["interval_start"]),
                buy=float(r["buy_price"]),
                sell=float(r["sell_price"]),
                pv_a_wh=float(r["pv_a_forecast_w"] or 0) * 0.25,
                pv_b_wh=float(r["pv_b_forecast_w"] or 0) * 0.25,
                load_wh=float(r["load_baseline_w"] or 0) * 0.25,
                grid_import_wh=0.0,
                grid_export_wh=0.0,
                soc_pct=None,
            )
        )
    soc0 = float(ctx["soc_wh"])
    return slots, bat, grid, soc0


def main() -> None:
    header = (
        f"{'fixture':<42} {'current':>9} {'clean':>9} {'Δ':>8}  pozn."
    )
    print("# Clean core prototyp vs současný plánovač (modelovaný cashflow, Kč/horizont)")
    print("# Δ < 0 = čisté jádro vydělá víc na stejných vstupech (bez SoC adjustu — terminal value v objective obou)")
    print()
    print(header)
    print("-" * len(header))
    total_cur = total_clean = 0.0
    for path in FIXTURES:
        fx = json.loads(path.read_text(encoding="utf-8"))
        slots, bat, grid, soc0 = _fixture_to_inputs(fx)
        avg_buy = sum(s.buy for s in slots[: 96]) / min(96, len(slots))
        factor = float(fx["context_json"]["battery"].get("planner_terminal_soc_value_factor") or 1.0)
        cash, soc_end = econ.solve_oracle(slots, bat, grid, soc0, avg_buy * factor)
        # SoC-fér: ocenit koncový SoC stejně jako terminal value
        clean_adj = cash - soc_end / 1000.0 * avg_buy * factor

        snap = json.loads((SNAPSHOTS / path.name).read_text(encoding="utf-8"))
        if "solver_error" in snap:
            print(f"{path.stem:<42} {'INFEAS':>9} {clean_adj:>9.1f} {'—':>8}  current selhal, clean OK")
            continue
        cur_cash = snap["totals"]["cashflow_czk"]
        cur_soc_end = snap["slots"][-1]["battery_soc_target"] / 100.0 * bat.usable_wh
        cur_adj = cur_cash - cur_soc_end / 1000.0 * avg_buy * factor
        d = clean_adj - cur_adj
        total_cur += cur_adj
        total_clean += clean_adj
        print(f"{path.stem:<42} {cur_adj:>9.1f} {clean_adj:>9.1f} {d:>8.1f}")
    print("-" * len(header))
    print(f"{'CELKEM (bez infeasible)':<42} {total_cur:>9.1f} {total_clean:>9.1f} {total_clean - total_cur:>8.1f}")


if __name__ == "__main__":
    main()
