#!/usr/bin/env python3
"""Track 0 -- lock the design space: enumerate every axis + a coverage map.

"Global optimum" is only meaningful against a bounded, enumerated space. This
defines that space (use cases x fluid architectures x system stack x objectives)
and then maps what has actually been scored vs estimated-only vs unscored -- so
coverage is provable by construction and the gaps are visible.

Authoritative spec (the enumeration) + a derived coverage matrix from the
existing scored outputs (Phase A/B + novel search). Computational screening only.
"""
from __future__ import annotations

import argparse
import json
from pathlib import Path
from typing import Any

ROOT = Path(__file__).resolve().parents[2]
DATA = ROOT / "data" / "processed"
LOCK = DATA / "lockspace"
OUT_JSON = LOCK / "design_space.json"
DOC = ROOT / "docs" / "DESIGN_SPACE_MAP.md"

# 8 objectives — toxicity is the SCORED 8th axis (GO5, from MatAtlas ToxValDB). Each USE_CASES weight
# vector sums to 1.0 over all 8 and carries a customer/standards citation (data/raw/customer_specs).
OBJECTIVES = ["chf", "dielectric", "safety", "gwp", "tb_window", "cost", "durability", "toxicity"]

_OCP = "OCP Immersion Fluid Spec (ε<2.3, GWP100<250, Tb 30-75 C)"
_ASHRAE = "ASHRAE 5th-Ed liquid classes W17-W45"
_NVDA = "NVIDIA/OCP direct-to-chip cold-plate guidance"

# Use cases: weight vector over the 8 OBJECTIVES (sum=1) + hard cutoffs + cited weights_source.
USE_CASES: dict[str, dict[str, Any]] = {
    "direct_to_chip": {
        "weights": {"chf": .25, "dielectric": .25, "safety": .12, "gwp": .08, "tb_window": .12, "cost": .05, "durability": .03, "toxicity": .10},
        "cutoffs": {"eps_max": 6, "gwp_max": 150, "tb_lo": 45, "tb_hi": 75, "nonflammable": True},
        "deployment": "two-phase cold plate at the die; lowest ε (exposed electronics), Tb just above junction idle",
        "weights_source": f"{_OCP}; {_NVDA}", "scored": True},
    "immersion_tank": {
        "weights": {"chf": .25, "dielectric": .12, "safety": .18, "gwp": .10, "tb_window": .08, "cost": .10, "durability": .05, "toxicity": .12},
        "cutoffs": {"eps_max": 15, "gwp_max": 150, "tb_lo": 55, "tb_hi": 95, "nonflammable": True},
        "deployment": "tank immersion, higher Tb / lower system pressure; open-bath human exposure → toxicity up",
        "weights_source": f"{_OCP} (immersion)", "scored": True},
    "two_phase_cold_plate": {
        "weights": {"chf": .22, "dielectric": .25, "safety": .12, "gwp": .08, "tb_window": .12, "cost": .06, "durability": .05, "toxicity": .10},
        "cutoffs": {"eps_max": 2.5, "gwp_max": 250, "tb_lo": 35, "tb_hi": 70, "nonflammable": True},
        "deployment": "pumped two-phase cold plate (the 2026-27 growth architecture); chf via FLOW-boiling "
                      "(low-μ wins, GO2), ε<2.3 for leak-safety on live electronics, Tb facility-water-matched "
                      "and pressure-tunable (GO3), tiny fluid charge → cost weight low",
        "weights_source": f"{_OCP}; {_ASHRAE}; {_NVDA}", "scored": False},
    "edge_outdoor": {
        "weights": {"safety": .28, "durability": .18, "cost": .18, "chf": .12, "dielectric": .08, "gwp": .05, "tb_window": .0, "toxicity": .11},
        "cutoffs": {"gwp_max": 150, "nonflammable": True},
        "deployment": "remote/edge, no active climate control; safety + durability + cost dominate; unattended → toxicity matters",
        "weights_source": "remote/unattended edge deployment (no on-site response)", "scored": False},
    "defense_space": {
        "weights": {"chf": .22, "durability": .18, "safety": .18, "tb_window": .13, "dielectric": .08, "gwp": .05, "cost": .04, "toxicity": .12},
        "cutoffs": {"gwp_max": 300, "nonflammable": True, "gravity_independent": True},
        "deployment": "microgravity/space; Marangoni self-pumping (gravity-independent); crewed → toxicity up",
        "weights_source": "crewed/microgravity (NASA/defense outgassing+tox limits)", "scored": False},
    "ev_battery": {
        "weights": {"safety": .26, "cost": .22, "durability": .18, "dielectric": .12, "chf": .08, "gwp": .0, "tb_window": .0, "toxicity": .14},
        "cutoffs": {"nonflammable": True},
        "deployment": "EV battery pack immersion; safety + cost + cycle durability; passenger-cabin proximity → toxicity high",
        "weights_source": "EV pack (passenger proximity, automotive safety)", "scored": False},
    "transformer": {
        "weights": {"dielectric": .28, "durability": .24, "safety": .18, "cost": .14, "chf": .08, "gwp": .0, "tb_window": .0, "toxicity": .08},
        "cutoffs": {"nonflammable": True},
        "deployment": "HV transformer / power-electronics; dielectric strength + long aging life; sealed → toxicity weight low",
        "weights_source": "HV transformer (sealed, IEC dielectric-fluid practice)", "scored": False},
}

ARCHITECTURES = {
    "binary": {"desc": "fuel + pump 2-component blend", "scored": True},
    "ternary": {"desc": "fuel + pump + 3rd component (boiling-window / dielectric tuner)", "scored": False},
    "fluid_plus_additive": {"desc": "base fluid + functional additive package (inhibitor/antioxidant/tracer)",
                             "scored": "partial"},
}

SYSTEM_STACK = {
    "enhancements": ["copper_foam", "biphilic", "nanoparticle", "ultrasound", "EHD"],
    "operating_params": ["foam_PPI", "ultrasound_freq", "ultrasound_duty", "nanoparticle_vol_pct",
                         "pressure", "subcooling", "fill_fraction"],
    "modeling_status": "FLUID_MATCHED (Track-1d wired: per-cell winners carry a matched copper-foam "
                       "PPI from the fluid capillary length + grid-searched nano/ultrasound via "
                       "hardware_library/enhancement_cards); pool-boiling validation NOT measured",
}

# scored-candidate sources (all current scoring is binary, deployment-objective ~ direct/immersion)
CANDIDATE_SOURCES = {
    "hardened_system_ranking": DATA / "phaseB" / "hardened_system_ranking.json",
    "flagship_system_verdict": DATA / "phaseA" / "flagship_system_verdict.json",
    "cascade_funnel": DATA / "phaseA" / "cascade_funnel.json",
    "novel_fuel_candidates": DATA / "novel_search" / "novel_fuel_candidates.json",
}


def _count_scored() -> dict[str, Any]:
    counts = {}
    try:
        counts["system_leads"] = len(json.loads(CANDIDATE_SOURCES["hardened_system_ranking"].read_text())["probabilistic_ranking"])
    except Exception:
        counts["system_leads"] = 0
    try:
        counts["flagships"] = len(json.loads(CANDIDATE_SOURCES["flagship_system_verdict"].read_text())["candidates"])
    except Exception:
        counts["flagships"] = 0
    try:
        cf = json.loads(CANDIDATE_SOURCES["cascade_funnel"].read_text())
        counts["binary_blends_screened"] = cf.get("T_all_blends")
        counts["nonflammable_lowgwp_finalists"] = cf.get("n_nonflammable_lowgwp_candidates")
    except Exception:
        pass
    try:
        nv = json.loads(CANDIDATE_SOURCES["novel_fuel_candidates"].read_text())["summary"]
        counts["novel_screened"] = nv.get("novel")
        counts["novel_viable_nonflammable_lowgwp"] = nv.get("in_target_track_nonflammable_lowgwp")
    except Exception:
        pass
    return counts


def _cell_scores() -> dict[str, Any] | None:
    """Per-cell scoring artifact from score_all_cells.py (full-population scoring), if present."""
    p = LOCK / "cell_scores.json"
    try:
        return json.loads(p.read_text())["cells"]
    except Exception:
        return None


def build() -> dict[str, Any]:
    # coverage matrix: use_case x architecture -> status. DERIVED from artifacts when the
    # full-population scoring (score_all_cells.py) has run; falls back to the original
    # flagship-level statuses otherwise (backward compatible).
    cell_scores = _cell_scores()
    matrix = {}
    for uc, ucd in USE_CASES.items():
        matrix[uc] = {}
        for arch, ad in ARCHITECTURES.items():
            if cell_scores and cell_scores.get(uc, {}).get(arch, {}).get("status") == "SCORED":
                status = "SCORED"
            elif arch == "binary" and ucd["scored"]:
                status = "SCORED"
            elif arch == "binary" and not ucd["scored"]:
                status = "UNSCORED_USE_CASE"          # objective exists, this use-case weighting not run
            elif arch == "ternary":
                status = "UNSCORED_ARCHITECTURE"
            else:  # fluid_plus_additive
                status = "PARTIAL_COMPATIBILITY_ONLY"
            matrix[uc][arch] = status

    cells = [(uc, arch) for uc in USE_CASES for arch in ARCHITECTURES]
    scored_cells = sum(1 for uc, a in cells if matrix[uc][a] == "SCORED")
    return {
        "spec": {
            "objectives": OBJECTIVES,
            "use_cases": USE_CASES,
            "architectures": ARCHITECTURES,
            "system_stack": SYSTEM_STACK,
        },
        "coverage_matrix": matrix,
        "coverage_summary": {
            "total_cells": len(cells),
            "scored_cells": scored_cells,
            "coverage_pct": round(100 * scored_cells / len(cells), 1),
            "scored_counts": _count_scored(),
            "gaps": {
                "unscored_use_cases": sorted({uc for uc in USE_CASES
                                              if any(matrix[uc][a] != "SCORED" for a in ARCHITECTURES)}),
                "unscored_architectures": sorted({a for a in ARCHITECTURES
                                                  if any(matrix[uc][a] != "SCORED" for uc in USE_CASES)}),
                "system_stack": "fluid x surface coupling now FLUID_MATCHED (Track-1d wired via "
                                "hardware_library); operating-point + pool-boiling validation NOT measured",
            },
        },
        "honesty": "Coverage = which cells have computational scoring, not measured proof. New objectives "
                   "cost/durability are placeholders (not yet computed). This map makes the gaps explicit.",
    }


def write_doc(v: dict[str, Any]) -> str:
    cs = v["coverage_summary"]
    archs = list(ARCHITECTURES.keys())
    lines = [
        "# Design-Space Map (Track 0) - the whole space, and what's covered", "",
        f"**Coverage: {cs['scored_cells']}/{cs['total_cells']} cells scored "
        f"({cs['coverage_pct']}%).** Computational scoring, not measured proof.", "",
        "## Coverage matrix (use case × fluid architecture)", "",
        "| Use case | " + " | ".join(archs) + " |",
        "|---|" + "|".join(["---"] * len(archs)) + "|",
    ]
    for uc in USE_CASES:
        row = " | ".join(v["coverage_matrix"][uc][a] for a in archs)
        lines.append(f"| {uc} | {row} |")
    lines += ["", "## Objectives", "", f"`{', '.join(OBJECTIVES)}` (cost & durability are placeholders).", ""]
    lines += ["## Use-case definitions (weights + cutoffs)", ""]
    for uc, d in USE_CASES.items():
        w = ", ".join(f"{k}={v2}" for k, v2 in d["weights"].items() if v2)
        all_scored = all(v["coverage_matrix"][uc][a] == "SCORED" for a in ARCHITECTURES)
        lines.append(f"- **{uc}** {'✅scored' if all_scored else '⬜ partially scored'} — {d['deployment']}. Weights: {w}.")
    lines += ["", "## The gaps (what 'global optimum' still requires)", "",
              f"- **Unscored use cases:** {', '.join(cs['gaps']['unscored_use_cases']) or f'none — all {len(USE_CASES)} scored (see PER_CELL_WINNERS.md)'}",
              f"- **Unscored architectures:** {', '.join(cs['gaps']['unscored_architectures']) or 'none — binary, ternary, and additive all scored'}",
              f"- **System stack:** {cs['gaps']['system_stack']}",
              "", f"> {v['honesty']}"]
    return "\n".join(lines) + "\n"


def main() -> None:
    ap = argparse.ArgumentParser(description=__doc__)
    ap.add_argument("--write", action="store_true")
    args = ap.parse_args()
    v = build()
    cs = v["coverage_summary"]
    print(f"Design-space lock: {cs['scored_cells']}/{cs['total_cells']} cells scored ({cs['coverage_pct']}%)")
    print(f"  use cases: {len(USE_CASES)} ({sum(1 for u in USE_CASES.values() if u['scored'])} scored)")
    print(f"  architectures: {list(ARCHITECTURES)}")
    print(f"  unscored use cases: {cs['gaps']['unscored_use_cases']}")
    print(f"  scored counts: {cs['scored_counts']}")
    if args.write:
        LOCK.mkdir(parents=True, exist_ok=True)
        OUT_JSON.write_text(json.dumps(v, indent=2))
        DOC.write_text(write_doc(v))
        print(f"  wrote {OUT_JSON}, {DOC}")


if __name__ == "__main__":
    main()
