"""Generate public-safe equipment and facilities teaching datasets.

The generated files are conceptual, precomputed replays for SemiAgora pages.
They avoid live facility operation, equipment control, maintenance instructions,
interlock settings, gas or chemical handling guidance, recipes, and signoff.
"""

from __future__ import annotations

import json
import math
from pathlib import Path


ROOT = Path(__file__).resolve().parent


SOURCES = [
    {
        "label": "ISO 14644-1 cleanroom particle classification",
        "url": "https://www.iso.org/standard/53394.html",
        "note": "Official ISO page anchoring cleanroom air-cleanliness vocabulary by airborne-particle concentration.",
    },
    {
        "label": "ISO 14644-4 cleanroom design and start-up",
        "url": "https://www.iso.org/standard/72379.html",
        "note": "Official ISO page anchoring cleanroom requirements, design, construction, start-up, verification, and life-cycle vocabulary.",
    },
    {
        "label": "SEMI E10 equipment RAM and utilization",
        "url": "https://www.semi.org/en/Standards/CTR_031244",
        "note": "Official SEMI page anchoring equipment reliability, availability, maintainability, and utilization language.",
    },
    {
        "label": "SEMI E10 and E79 equipment performance metrics",
        "url": "https://www.semi.org/en/products-services/standards/step/equipment-performance-metrics",
        "note": "Official SEMI education page linking RAM, utilization, OEE, and throughput vocabulary.",
    },
    {
        "label": "NIST manufacturing machinery maintenance",
        "url": "https://www.nist.gov/el/applied-economics-office/manufacturing/topics-manufacturing/manufacturing-machinery-maintenance",
        "note": "Official NIST page anchoring maintenance-method vocabulary for smart manufacturing.",
    },
    {
        "label": "NIST CHIPS research and development programs",
        "url": "https://www.nist.gov/chips/research-development-programs",
        "note": "Official NIST page anchoring semiconductor metrology, standards, and precision-equipment calibration context.",
    },
    {
        "label": "NIST CHIPS materials and manufacturing equipment facilities",
        "url": "https://www.nist.gov/chips/chips-incentives-funding-opportunities/facilities-for-semiconductor-materials-and",
        "note": "Official NIST page defining semiconductor materials and manufacturing-equipment facilities for public program vocabulary.",
    },
    {
        "label": "ASML EUV lithography systems",
        "url": "https://www.asml.com/en/products/euv-lithography-systems",
        "note": "Official ASML page anchoring high-vacuum and precision-wafer-handler vocabulary for EUV tools.",
    },
    {
        "label": "SEMI smart manufacturing sustainability",
        "url": "https://www.semi.org/en/blogs/technology-and-trends/accelerating-sustainability-with-smart-manufacturing-in-semiconductors%E2%80%93session-at-semicon-west-2024",
        "note": "Official SEMI page anchoring smart manufacturing, sustainability, and efficiency vocabulary.",
    },
]


LIMITS = [
    "No facility operation, tool-control command, endpoint setting, interlock setting, emergency procedure, or maintenance instruction is included.",
    "No gas, chemical, exhaust, abatement, electrical, UPW, vacuum, or HVAC handling guidance is included.",
    "No process recipe, process window, qualified PM plan, production dispatch rule, capacity commitment, or safety signoff is included.",
    "All numbers are educational proxies and must be replaced by qualified facility, EHS, equipment, and process-owner evidence before engineering decisions.",
]


def write_json(filename: str, payload: dict) -> None:
    (ROOT / filename).write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8")


def monotonic_increase(values: list[float]) -> bool:
    return all(values[i] <= values[i + 1] for i in range(len(values) - 1))


def monotonic_decrease(values: list[float]) -> bool:
    return all(values[i] >= values[i + 1] for i in range(len(values) - 1))


def common_payload(experiment_id: str, title: str, model_boundary: str) -> dict:
    return {
        "schema": "semiagora.eqp-simulation.v1",
        "experiment_id": experiment_id,
        "title": title,
        "execution_mode": "precomputed-only",
        "model_boundary": model_boundary,
        "limitations": LIMITS,
        "sources": SOURCES,
    }


def cleanroom_particle_budget() -> None:
    cases = [
        {
            "label": "gowning_entry_high_traffic",
            "traffic_index": 0.82,
            "air_change_proxy": 0.62,
            "particle_burden_proxy": 0.78,
            "classification_margin_proxy": 0.18,
            "excursion_risk_proxy": 0.64,
        },
        {
            "label": "stable_bay_nominal",
            "traffic_index": 0.42,
            "air_change_proxy": 0.78,
            "particle_burden_proxy": 0.36,
            "classification_margin_proxy": 0.52,
            "excursion_risk_proxy": 0.27,
        },
        {
            "label": "mini_environment_low_traffic",
            "traffic_index": 0.18,
            "air_change_proxy": 0.91,
            "particle_burden_proxy": 0.14,
            "classification_margin_proxy": 0.76,
            "excursion_risk_proxy": 0.11,
        },
        {
            "label": "post_activity_recovery_check",
            "traffic_index": 0.55,
            "air_change_proxy": 0.84,
            "particle_burden_proxy": 0.28,
            "classification_margin_proxy": 0.61,
            "excursion_risk_proxy": 0.19,
        },
    ]
    payload = common_payload(
        "SA-EQP-CLEANROOM-PARTICLE-001",
        "Cleanroom particle budget replay",
        "Educational cleanroom-particle budget replay for vocabulary and trend literacy; not a cleanroom certification, monitoring plan, or facility operating procedure.",
    )
    payload.update(
        {
            "axes": ["traffic teaching case", "air-change proxy", "particle burden proxy"],
            "cases": cases,
            "derived_metrics": {
                "lowest_particle_burden_case": "mini_environment_low_traffic",
                "lowest_particle_burden_proxy": 0.14,
                "highest_margin_case": "mini_environment_low_traffic",
                "traffic_to_stable_burden_delta": round(
                    cases[0]["particle_burden_proxy"] - cases[1]["particle_burden_proxy"], 2
                ),
            },
            "verification": {
                "case_count": len(cases),
                "particle_burden_lower_for_mini_environment": cases[2]["particle_burden_proxy"]
                < cases[1]["particle_burden_proxy"]
                < cases[0]["particle_burden_proxy"],
                "recovery_case_improves_after_activity": cases[3]["particle_burden_proxy"]
                < cases[0]["particle_burden_proxy"],
                "all_cases_precomputed": True,
            },
        }
    )
    payload["verification"]["margin_increases_as_burden_decreases"] = monotonic_increase(
        [
            cases[0]["classification_margin_proxy"],
            cases[1]["classification_margin_proxy"],
            cases[2]["classification_margin_proxy"],
        ]
    )
    write_json("eqp-cleanroom-particle-budget-web-v1.json", payload)


def vacuum_pumpdown_envelope() -> None:
    cases = [
        {
            "label": "loadlock_fast_roughing_teaching_case",
            "volume_l": 42,
            "effective_speed_l_s": 24,
            "leak_floor_torr": 0.003,
            "target_torr": 0.05,
        },
        {
            "label": "process_chamber_nominal_teaching_case",
            "volume_l": 180,
            "effective_speed_l_s": 36,
            "leak_floor_torr": 0.0008,
            "target_torr": 0.01,
        },
        {
            "label": "large_chamber_recovery_teaching_case",
            "volume_l": 420,
            "effective_speed_l_s": 48,
            "leak_floor_torr": 0.0015,
            "target_torr": 0.02,
        },
    ]
    start_torr = 760.0
    for case in cases:
        tau = case["volume_l"] / case["effective_speed_l_s"]
        ratio = (case["target_torr"] - case["leak_floor_torr"]) / (start_torr - case["leak_floor_torr"])
        case["time_to_target_s"] = round(-tau * math.log(ratio), 1)
        case["time_constant_s"] = round(tau, 2)
        case["normalized_recovery_index"] = round(case["time_to_target_s"] / cases[0]["volume_l"], 3)
    payload = common_payload(
        "SA-EQP-VACUUM-PUMPDOWN-001",
        "Vacuum pumpdown envelope",
        "Educational pumpdown-envelope replay for reading volume, effective speed, target pressure, and leak-floor vocabulary; not a pump selection, leak-check, or tool-operation procedure.",
    )
    payload.update(
        {
            "axes": ["teaching chamber case", "target pressure", "time to target"],
            "start_pressure_torr": start_torr,
            "cases": cases,
            "derived_metrics": {
                "fastest_case": min(cases, key=lambda item: item["time_to_target_s"])["label"],
                "slowest_case": max(cases, key=lambda item: item["time_to_target_s"])["label"],
                "nominal_chamber_time_s": cases[1]["time_to_target_s"],
                "large_to_loadlock_time_ratio": round(cases[2]["time_to_target_s"] / cases[0]["time_to_target_s"], 2),
            },
            "verification": {
                "case_count": len(cases),
                "positive_times": all(case["time_to_target_s"] > 0 for case in cases),
                "large_chamber_slowest": cases[2]["time_to_target_s"] > cases[1]["time_to_target_s"] > cases[0]["time_to_target_s"],
                "targets_above_floor": all(case["target_torr"] > case["leak_floor_torr"] for case in cases),
                "all_cases_precomputed": True,
            },
        }
    )
    write_json("eqp-vacuum-pumpdown-envelope-web-v1.json", payload)


def tool_availability_queue() -> None:
    cases = [
        {
            "label": "baseline_tool_week",
            "productive_h": 103,
            "standby_h": 18,
            "engineering_h": 9,
            "scheduled_downtime_h": 18,
            "unscheduled_downtime_h": 20,
            "queue_wait_h": 31,
        },
        {
            "label": "ram_improvement_week",
            "productive_h": 122,
            "standby_h": 15,
            "engineering_h": 8,
            "scheduled_downtime_h": 15,
            "unscheduled_downtime_h": 8,
            "queue_wait_h": 17,
        },
        {
            "label": "dispatch_pressure_week",
            "productive_h": 126,
            "standby_h": 8,
            "engineering_h": 10,
            "scheduled_downtime_h": 12,
            "unscheduled_downtime_h": 12,
            "queue_wait_h": 28,
        },
    ]
    total_h = 168
    for case in cases:
        available_h = total_h - case["scheduled_downtime_h"] - case["unscheduled_downtime_h"]
        case["availability_percent"] = round(100 * available_h / total_h, 2)
        case["utilization_percent"] = round(100 * case["productive_h"] / total_h, 2)
        case["downtime_percent"] = round(
            100 * (case["scheduled_downtime_h"] + case["unscheduled_downtime_h"]) / total_h, 2
        )
        case["queue_pressure_index"] = round(case["queue_wait_h"] / max(case["productive_h"], 1), 3)
    payload = common_payload(
        "SA-EQP-TOOL-AVAILABILITY-001",
        "Tool availability and queue state",
        "Educational SEMI E10-style state replay for RAM, utilization, and queue vocabulary; not a dispatch rule, maintenance plan, or production commitment.",
    )
    payload.update(
        {
            "axes": ["weekly state teaching case"],
            "total_hours": total_h,
            "cases": cases,
            "derived_metrics": {
                "baseline_availability_percent": cases[0]["availability_percent"],
                "improved_availability_percent": cases[1]["availability_percent"],
                "unscheduled_downtime_reduction_h": cases[0]["unscheduled_downtime_h"]
                - cases[1]["unscheduled_downtime_h"],
                "dispatch_pressure_queue_index": cases[2]["queue_pressure_index"],
            },
            "verification": {
                "case_count": len(cases),
                "all_state_hours_fit_week": all(
                    case["productive_h"]
                    + case["standby_h"]
                    + case["engineering_h"]
                    + case["scheduled_downtime_h"]
                    + case["unscheduled_downtime_h"]
                    == total_h
                    for case in cases
                ),
                "ram_case_improves_availability": cases[1]["availability_percent"] > cases[0]["availability_percent"],
                "ram_case_reduces_queue_pressure": cases[1]["queue_pressure_index"] < cases[0]["queue_pressure_index"],
                "dispatch_pressure_has_higher_queue_than_ram_case": cases[2]["queue_pressure_index"]
                > cases[1]["queue_pressure_index"],
            },
        }
    )
    write_json("eqp-tool-availability-queue-web-v1.json", payload)


def utilities_load_budget() -> None:
    cases = [
        {
            "label": "baseline_mixed_process_bay",
            "cleanroom_air_mwh": 42,
            "process_vacuum_exhaust_mwh": 21,
            "upw_process_cooling_mwh": 18,
            "lithography_precision_support_mwh": 16,
            "abatement_and_scrub_mwh": 9,
        },
        {
            "label": "high_vacuum_lithography_shift",
            "cleanroom_air_mwh": 45,
            "process_vacuum_exhaust_mwh": 32,
            "upw_process_cooling_mwh": 19,
            "lithography_precision_support_mwh": 28,
            "abatement_and_scrub_mwh": 11,
        },
        {
            "label": "wet_process_heavy_shift",
            "cleanroom_air_mwh": 43,
            "process_vacuum_exhaust_mwh": 18,
            "upw_process_cooling_mwh": 31,
            "lithography_precision_support_mwh": 14,
            "abatement_and_scrub_mwh": 13,
        },
    ]
    for case in cases:
        total = sum(value for key, value in case.items() if key.endswith("_mwh"))
        case["total_teaching_load_mwh"] = total
        case["cleanroom_air_share_percent"] = round(100 * case["cleanroom_air_mwh"] / total, 1)
        case["utility_stress_index"] = round(
            (
                case["process_vacuum_exhaust_mwh"]
                + case["upw_process_cooling_mwh"]
                + case["lithography_precision_support_mwh"]
            )
            / total,
            3,
        )
    payload = common_payload(
        "SA-EQP-UTILITIES-LOAD-001",
        "Facility utilities load budget",
        "Educational facility-utilities load replay for reading load lanes and sustainability vocabulary; not a facilities design, electrical plan, UPW plan, exhaust plan, or operating instruction.",
    )
    payload.update(
        {
            "axes": ["process mix teaching case", "normalized utility lane"],
            "cases": cases,
            "derived_metrics": {
                "baseline_total_mwh": cases[0]["total_teaching_load_mwh"],
                "highest_total_case": max(cases, key=lambda item: item["total_teaching_load_mwh"])["label"],
                "highest_stress_case": max(cases, key=lambda item: item["utility_stress_index"])["label"],
                "wet_shift_upw_share_percent": round(
                    100 * cases[2]["upw_process_cooling_mwh"] / cases[2]["total_teaching_load_mwh"], 1
                ),
            },
            "verification": {
                "case_count": len(cases),
                "totals_match_lane_sum": all(
                    case["total_teaching_load_mwh"]
                    == case["cleanroom_air_mwh"]
                    + case["process_vacuum_exhaust_mwh"]
                    + case["upw_process_cooling_mwh"]
                    + case["lithography_precision_support_mwh"]
                    + case["abatement_and_scrub_mwh"]
                    for case in cases
                ),
                "lithography_shift_highest_total": cases[1]["total_teaching_load_mwh"]
                > cases[2]["total_teaching_load_mwh"]
                > cases[0]["total_teaching_load_mwh"],
                "wet_shift_highest_upw_lane": cases[2]["upw_process_cooling_mwh"]
                > cases[1]["upw_process_cooling_mwh"]
                > cases[0]["upw_process_cooling_mwh"],
                "all_cases_precomputed": True,
            },
        }
    )
    write_json("eqp-utilities-load-budget-web-v1.json", payload)


def main() -> None:
    cleanroom_particle_budget()
    vacuum_pumpdown_envelope()
    tool_availability_queue()
    utilities_load_budget()


if __name__ == "__main__":
    main()
