from __future__ import annotations

import csv
import json
from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib import font_manager

ROOT = Path(__file__).resolve().parent
FIGURES = ROOT / "figures"
TABLES = ROOT / "tables"
FIGURES.mkdir(exist_ok=True)
TABLES.mkdir(exist_ok=True)

LOCAL_FONT = ROOT.parent.parent / "public" / "fonts" / "NotoSansTC-Regular.ttf"
if LOCAL_FONT.exists():
    font_manager.fontManager.addfont(LOCAL_FONT)

plt.rcParams.update({
    "font.family": ["Noto Sans TC", "DejaVu Sans"],
    "axes.unicode_minus": False,
    "figure.dpi": 160,
})


def load_assumptions() -> pd.DataFrame:
    frame = pd.read_csv(ROOT / "assumptions.csv")
    return frame.set_index("parameter")


def val(frame: pd.DataFrame, parameter: str, scenario: str) -> float:
    return float(frame.loc[parameter, scenario])


def scenario_row(frame: pd.DataFrame, scenario: str) -> dict:
    area = val(frame, "total_canopy_area", scenario)
    ppfd = val(frame, "ppfd", scenario)
    hours = val(frame, "photoperiod", scenario)
    days = val(frame, "cycle_days", scenario)
    ppe = val(frame, "led_ppe", scenario)
    multiplier = val(frame, "facility_multiplier", scenario)
    yield_density = val(frame, "grain_yield", scenario)
    turnaround = val(frame, "turnaround_days", scenario)

    lighting_kw = ppfd * area / ppe / 1000
    lighting_kwh_cycle = lighting_kw * hours * days
    facility_kwh_cycle = lighting_kwh_cycle * multiplier
    grain_kg_cycle = area * yield_density
    kwh_per_kg = facility_kwh_cycle / grain_kg_cycle
    cycles_per_year = 365 / (days + turnaround)
    grain_kg_year = grain_kg_cycle * cycles_per_year
    dli = ppfd * hours * 0.0036

    row = {
        "scenario": scenario,
        "canopy_area_m2": area,
        "ppfd_umol_m2_s": ppfd,
        "photoperiod_h_day": hours,
        "dli_mol_m2_day": dli,
        "cycle_days": days,
        "led_ppe_umol_J": ppe,
        "facility_multiplier": multiplier,
        "grain_yield_kg_m2_cycle": yield_density,
        "lighting_power_kw": lighting_kw,
        "lighting_kwh_cycle": lighting_kwh_cycle,
        "facility_kwh_cycle": facility_kwh_cycle,
        "grain_kg_cycle": grain_kg_cycle,
        "kwh_per_kg": kwh_per_kg,
        "cycles_per_year": cycles_per_year,
        "grain_kg_year": grain_kg_year,
    }
    for price in (3, 5, 7):
        row[f"electricity_floor_twd_kg_at_{price}"] = kwh_per_kg * price
    return row


def save_system_boundary() -> None:
    fig, ax = plt.subplots(figsize=(11, 5.8))
    ax.set_xlim(0, 11)
    ax.set_ylim(0, 6)
    ax.axis("off")
    boxes = [
        (0.5, 3.7, 2.1, 1.2, "電力輸入\nLED・冷卻・除濕・泵"),
        (3.1, 3.7, 2.1, 1.2, "貨櫃環境\n光・溫濕度・CO₂"),
        (5.7, 3.7, 2.1, 1.2, "水耕根域\npH・EC・溶氧"),
        (8.3, 3.7, 2.1, 1.2, "生物產出\n稻穀・秸稈・種子"),
        (3.1, 1.2, 2.1, 1.2, "外部條件\n氣候・電價・設備效率"),
        (5.7, 1.2, 2.1, 1.2, "本研究輸出\nkWh/kg・元/kg下限\nkg/櫃/年"),
    ]
    for x, y, w, h, label in boxes:
        color = "#f4e8da" if y > 3 else "#e8f1eb"
        ax.add_patch(plt.Rectangle((x, y), w, h, facecolor=color, edgecolor="#6f3519", lw=1.4))
        ax.text(x + w / 2, y + h / 2, label, ha="center", va="center", fontsize=11, color="#241913")
    arrow = dict(arrowstyle="->", color="#6f3519", lw=1.6)
    for x in (2.6, 5.2, 7.8):
        ax.annotate("", xy=(x + 0.45, 4.3), xytext=(x, 4.3), arrowprops=arrow)
    ax.annotate("", xy=(4.15, 3.65), xytext=(4.15, 2.45), arrowprops=arrow)
    ax.annotate("", xy=(6.75, 2.45), xytext=(6.75, 3.65), arrowprops=arrow)
    ax.text(5.5, 5.55, "圖1　水稻貨櫃化栽培之系統邊界", ha="center", fontsize=15, weight="bold", color="#241913")
    ax.text(5.5, 0.45, "不包含：設備資本支出、土地、人工、融資、碾米、包裝與配送；因此成本為電力成本下限。", ha="center", fontsize=9.5, color="#625b55")
    fig.tight_layout()
    fig.savefig(FIGURES / "figure-1-system-boundary.png", bbox_inches="tight")
    plt.close(fig)


def save_scenario_chart(frame: pd.DataFrame) -> None:
    labels = ["樂觀", "基準", "保守"]
    colors = ["#5f8f73", "#bb7b3e", "#7b3f2b"]
    fig, axes = plt.subplots(1, 2, figsize=(11, 5.4))
    axes[0].bar(labels, frame["kwh_per_kg"], color=colors)
    axes[0].set_ylabel("設施用電（kWh／kg 稻穀）")
    axes[0].set_title("單位稻穀用電")
    for i, value in enumerate(frame["kwh_per_kg"]):
        axes[0].text(i, value, f"{value:,.0f}", ha="center", va="bottom", fontsize=10)

    axes[1].bar(labels, frame["grain_kg_year"], color=colors)
    axes[1].set_ylabel("年產稻穀（kg／貨櫃）")
    axes[1].set_title("理論年產量（含每批7日整備）")
    for i, value in enumerate(frame["grain_kg_year"]):
        axes[1].text(i, value, f"{value:,.0f}", ha="center", va="bottom", fontsize=10)
    fig.suptitle("圖2　三種工程情境的能源與產能結果", fontsize=15, weight="bold")
    fig.text(0.5, 0.01, "情境值並非實地貨櫃試驗結果；用途是呈現參數組合下的量級。", ha="center", fontsize=9, color="#625b55")
    fig.tight_layout(rect=(0, 0.04, 1, 0.93))
    fig.savefig(FIGURES / "figure-2-scenario-results.png", bbox_inches="tight")
    plt.close(fig)


def save_heatmap() -> pd.DataFrame:
    ppfd_values = [350, 500, 700, 900]
    yield_values = [0.45, 0.65, 0.90, 1.20]
    hours, days, ppe, multiplier = 12, 95, 3.2, 1.55
    matrix = np.zeros((len(yield_values), len(ppfd_values)))
    for i, yield_density in enumerate(yield_values):
        for j, ppfd in enumerate(ppfd_values):
            matrix[i, j] = (ppfd / ppe / 1000 * hours * days * multiplier) / yield_density
    output = pd.DataFrame(matrix, index=yield_values, columns=ppfd_values)
    output.index.name = "grain_yield_kg_m2_cycle"
    output.to_csv(TABLES / "sensitivity-kwh-per-kg.csv", float_format="%.2f")

    fig, ax = plt.subplots(figsize=(9.6, 5.6))
    image = ax.imshow(matrix, cmap="YlOrBr", aspect="auto")
    ax.set_xticks(range(len(ppfd_values)), ppfd_values)
    ax.set_yticks(range(len(yield_values)), [f"{v:.2f}" for v in yield_values])
    ax.set_xlabel("PPFD（umol/m2/s）")
    ax.set_ylabel("每批稻穀產量（kg/m2）")
    ax.set_title("圖3　光強度與產量對單位用電的敏感度")
    for i in range(matrix.shape[0]):
        for j in range(matrix.shape[1]):
            color = "white" if matrix[i, j] > matrix.max() * 0.55 else "#211a15"
            ax.text(j, i, f"{matrix[i, j]:,.0f}\nkWh/kg", ha="center", va="center", color=color, fontsize=9)
    fig.colorbar(image, ax=ax, label="kWh／kg 稻穀")
    fig.text(0.5, 0.01, "固定條件：12 小時／日、95 日、PPE 3.2 umol/J、設施倍率 1.55。", ha="center", fontsize=9, color="#625b55")
    fig.tight_layout(rect=(0, 0.04, 1, 1))
    fig.savefig(FIGURES / "figure-3-sensitivity-heatmap.png", bbox_inches="tight")
    plt.close(fig)
    return output


def save_cost_chart(frame: pd.DataFrame) -> None:
    labels = ["樂觀", "基準", "保守"]
    x = np.arange(len(labels))
    width = 0.23
    fig, ax = plt.subplots(figsize=(10.5, 5.8))
    for offset, price, color in [(-width, 3, "#7fa58c"), (0, 5, "#bb7b3e"), (width, 7, "#7b3f2b")]:
        values = frame[f"electricity_floor_twd_kg_at_{price}"]
        bars = ax.bar(x + offset, values, width, label=f"{price} 元/kWh", color=color)
        for bar, value in zip(bars, values):
            ax.text(bar.get_x() + bar.get_width()/2, bar.get_height(), f"{value:,.0f}", ha="center", va="bottom", fontsize=8, rotation=90)
    ax.set_xticks(x, labels)
    ax.set_ylabel("電力成本下限（新臺幣元／kg 稻穀）")
    ax.set_title("圖4　電價敏感度：尚未計入設備、人工、耗材與碾米")
    ax.legend()
    ax.margins(y=0.18)
    fig.tight_layout()
    fig.savefig(FIGURES / "figure-4-electricity-cost-floor.png", bbox_inches="tight")
    plt.close(fig)


def save_use_case_matrix() -> None:
    uses = ["一般商品米", "高價展示／教育", "育種世代加速", "種原保存與研究", "極端環境／太空研究"]
    criteria = ["價格承受力", "週期價值", "環境控制價值", "規模匹配"]
    scores = np.array([
        [1, 1, 2, 1],
        [3, 2, 4, 3],
        [4, 5, 5, 5],
        [4, 4, 5, 4],
        [5, 5, 5, 4],
    ])
    pd.DataFrame(scores, index=uses, columns=criteria).to_csv(TABLES / "use-case-screening.csv")
    fig, ax = plt.subplots(figsize=(9.8, 5.8))
    im = ax.imshow(scores, cmap="RdYlGn", vmin=1, vmax=5, aspect="auto")
    ax.set_xticks(range(len(criteria)), criteria)
    ax.set_yticks(range(len(uses)), uses)
    for i in range(scores.shape[0]):
        for j in range(scores.shape[1]):
            ax.text(j, i, str(scores[i, j]), ha="center", va="center", fontsize=11, weight="bold")
    ax.set_title("圖5　用途適配度的決策篩選（1低、5高）")
    fig.colorbar(im, ax=ax, ticks=[1, 2, 3, 4, 5])
    fig.text(0.5, 0.01, "此矩陣是依本研究成本與技術條件形成的規範性判斷，不是問卷或市場實測結果。", ha="center", fontsize=9, color="#625b55")
    fig.tight_layout(rect=(0, 0.04, 1, 1))
    fig.savefig(FIGURES / "figure-5-use-case-screening.png", bbox_inches="tight")
    plt.close(fig)


def main() -> None:
    assumptions = load_assumptions()
    rows = [scenario_row(assumptions, name) for name in ("optimistic", "baseline", "conservative")]
    scenarios = pd.DataFrame(rows)
    scenarios.to_csv(ROOT / "scenario-results.csv", index=False, float_format="%.4f")
    scenarios.to_csv(TABLES / "scenario-summary.csv", index=False, float_format="%.2f")

    save_system_boundary()
    save_scenario_chart(scenarios)
    sensitivity = save_heatmap()
    save_cost_chart(scenarios)
    save_use_case_matrix()

    result = {
        "report_number": "SHWRP-2026-012",
        "version": "1.0",
        "analysis_date": "2026-07-15",
        "status": "public working paper; not externally peer reviewed",
        "system_boundary": "electricity-cost floor only; excludes capex, labor, financing, nutrients, water treatment, milling, packaging and distribution",
        "scenarios": rows,
        "sensitivity": {
            "minimum_kwh_per_kg": float(sensitivity.to_numpy().min()),
            "maximum_kwh_per_kg": float(sensitivity.to_numpy().max()),
            "fixed_conditions": {"photoperiod_h": 12, "cycle_days": 95, "ppe_umol_J": 3.2, "facility_multiplier": 1.55},
        },
        "integrity_checks": {
            "all_positive": bool((scenarios.select_dtypes(include="number") > 0).all().all()),
            "cost_monotonic_by_tariff": bool((scenarios["electricity_floor_twd_kg_at_3"] < scenarios["electricity_floor_twd_kg_at_5"]).all() and (scenarios["electricity_floor_twd_kg_at_5"] < scenarios["electricity_floor_twd_kg_at_7"]).all()),
            "scenario_order_kwh": bool(scenarios["kwh_per_kg"].is_monotonic_increasing),
        },
    }
    (ROOT / "analysis-results.json").write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8")
    print(json.dumps(result, ensure_ascii=False, indent=2))


if __name__ == "__main__":
    main()
