#!/usr/bin/env python3
"""Educational surface-treatment model summaries for SemiAgora process replays.

The generated datasets teach surface-cleaning observables such as contact
angle, organic-residue proxy, hydroxylation proxy, and recovery after air
exposure. They do not contain a qualified recipe, tool setting, or facility
procedure.
"""

from __future__ import annotations

import json
import math
from pathlib import Path


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


SOURCES = {
    "harrick": {
        "label": "Harrick Plasma surface chemistry modification",
        "url": "https://harrickplasma.com/surface-chemistry/",
        "note": "Public application note for plasma-driven surface chemistry, wettability, hydroxylation, and air recovery boundaries.",
    },
    "uiuc_uv_ozone": {
        "label": "UIUC MRL UV/Ozone Cleaner",
        "url": "https://mrl.illinois.edu/facilities/equipment/uvozone-cleaner",
        "note": "Public cleanroom equipment page describing UV/O3 organic-contaminant removal, hydrophilicity, and deposition-prep use.",
    },
    "unc_o2_plasma": {
        "label": "UNC CHANL oxygen plasma system",
        "url": "https://chanl.unc.edu/instrument/oxygen-plasma-system/",
        "note": "Public cleanroom instrument page listing surface activation, cleaning, wettability alteration, and adhesion preparation.",
    },
    "samco_uv_ozone": {
        "label": "Samco UV ozone surface treatment",
        "url": "https://www.samcointl.com/processes/surface-treatment/uv-ozone/",
        "note": "Public industry page for UV/O3 surface treatment and organic-contaminant removal vocabulary.",
    },
}


def decay_to_floor(initial: float, floor: float, rate: float, dose: float) -> float:
    return floor + (initial - floor) * math.exp(-rate * dose)


def round_list(values: list[float], digits: int = 3) -> list[float]:
    return [round(value, digits) for value in values]


def build_o2_plasma() -> dict:
    time_s = [0, 5, 10, 20, 30, 60, 120]
    configs = [
        ("gentle-o2-plasma", 0.55, "lower ion exposure, slower activation"),
        ("nominal-o2-plasma", 1.00, "fast hydrophilic activation teaching reference"),
        ("extended-o2-plasma", 1.60, "strong activation with louder ion-exposure warning"),
    ]
    cases = []
    for case_id, dose_scale, intent in configs:
        contact = [decay_to_floor(72.0, 6.0, 0.11, t * dose_scale) for t in time_s]
        hydroxyl = [100.0 * (1 - math.exp(-0.075 * t * dose_scale)) for t in time_s]
        organic_residue = [100.0 * math.exp(-0.055 * t * dose_scale) for t in time_s]
        risk = [100.0 * (1 - math.exp(-t * dose_scale / 210.0)) for t in time_s]
        cases.append(
            {
                "id": case_id,
                "intent": intent,
                "normalized_rf_dose": dose_scale,
                "time_s": time_s,
                "water_contact_angle_deg": round_list(contact, 2),
                "hydroxylation_proxy_percent": round_list(hydroxyl, 1),
                "organic_residue_proxy_percent": round_list(organic_residue, 1),
                "ion_exposure_risk_index": round_list(risk, 1),
            }
        )

    age_h = [0, 0.25, 1, 4, 24, 72]
    immediate_angle = cases[1]["water_contact_angle_deg"][5]
    recovered_angle = 54.0
    recovery = [
        immediate_angle + (recovered_angle - immediate_angle) * (1 - math.exp(-math.sqrt(max(h, 0.0)) / 2.7))
        for h in age_h
    ]

    return {
        "schema": "semiagora.process-surface-treatment.v1",
        "experiment_id": "SA-PROC-SURF-O2-PLASMA-001",
        "title": "O2 plasma surface activation replay",
        "execution_mode": "precomputed-only",
        "engine": "SemiAgora surface-treatment kinetics model",
        "model_boundary": "Educational contact-angle and residue-proxy replay; not a plasma tool recipe, wafer qualification, or measured SemiAgora process.",
        "observables": [
            "water contact angle",
            "hydroxylation proxy",
            "organic residue proxy",
            "ion exposure risk index",
            "hydrophobic recovery after air exposure",
        ],
        "cases": cases,
        "air_recovery": {
            "after_nominal_60_s_treatment": True,
            "age_h": age_h,
            "water_contact_angle_deg": round_list(recovery, 2),
            "note": "Recovery is included so learners do not treat activated surfaces as permanent.",
        },
        "derived_metrics": {
            "nominal_contact_angle_30_s_deg": cases[1]["water_contact_angle_deg"][4],
            "nominal_contact_angle_60_s_deg": cases[1]["water_contact_angle_deg"][5],
            "nominal_organic_removal_60_s_percent": round(100 - cases[1]["organic_residue_proxy_percent"][5], 1),
            "nominal_recovered_contact_angle_24_h_deg": round_list(recovery, 2)[4],
            "extended_ion_exposure_risk_120_s_index": cases[2]["ion_exposure_risk_index"][-1],
        },
        "verification": {
            "cases": len(cases),
            "contact_angle_decreases_with_time": all(
                all(a >= b for a, b in zip(case["water_contact_angle_deg"], case["water_contact_angle_deg"][1:]))
                for case in cases
            ),
            "organic_residue_decreases_with_time": all(
                all(a >= b for a, b in zip(case["organic_residue_proxy_percent"], case["organic_residue_proxy_percent"][1:]))
                for case in cases
            ),
            "risk_increases_with_time": all(
                all(a <= b for a, b in zip(case["ion_exposure_risk_index"], case["ion_exposure_risk_index"][1:]))
                for case in cases
            ),
            "air_recovery_increases_contact_angle": all(a <= b for a, b in zip(round_list(recovery, 2), round_list(recovery, 2)[1:])),
        },
        "sources": [SOURCES["harrick"], SOURCES["unc_o2_plasma"], SOURCES["uiuc_uv_ozone"]],
        "limitations": [
            "No plasma density, sheath physics, ion energy distribution, chamber pressure, gas flow, sample temperature, endpoint signal, or material-specific etch rate is modeled.",
            "The RF-dose labels are normalized teaching axes and must not be copied into facility settings.",
            "Contact-angle values are synthetic teaching replays, not measured wafer data.",
            "Real use requires material compatibility, charging and damage review, metrology, safety approval, and tool-specific training.",
        ],
    }


def build_o3_ozone() -> dict:
    time_min = [0, 1, 3, 5, 10, 15, 20, 30]
    configs = [
        ("dry-uv-o3", 1.0, "UV/O3 dry clean and activation replay"),
        ("di-o3-water", 0.7, "ozonated-water organic removal teaching replay"),
    ]
    cases = []
    for case_id, strength, intent in configs:
        floor = 10.0 if case_id == "dry-uv-o3" else 18.0
        contact = [decay_to_floor(72.0, floor, 0.25, t * strength) for t in time_min]
        organic_residue = [100.0 * math.exp(-0.22 * t * strength) for t in time_min]
        hydroxyl = [100.0 * (1 - math.exp(-0.20 * t * strength)) for t in time_min]
        oxide_proxy = [0.015 * t * strength + 0.18 * (1 - math.exp(-0.20 * t * strength)) for t in time_min]
        cases.append(
            {
                "id": case_id,
                "intent": intent,
                "normalized_oxidant_strength": strength,
                "time_min": time_min,
                "water_contact_angle_deg": round_list(contact, 2),
                "organic_residue_proxy_percent": round_list(organic_residue, 1),
                "hydroxylation_proxy_percent": round_list(hydroxyl, 1),
                "oxide_growth_proxy_nm": round_list(oxide_proxy, 3),
            }
        )

    dry = cases[0]
    water = cases[1]
    return {
        "schema": "semiagora.process-surface-treatment.v1",
        "experiment_id": "SA-PROC-SURF-O3-OZONE-001",
        "title": "O3 ozone and UV-ozone surface clean replay",
        "execution_mode": "precomputed-only",
        "engine": "SemiAgora surface-treatment kinetics model",
        "model_boundary": "Educational ozone/UV-O3 organic-removal and activation replay; not an ozone process recipe, wafer qualification, or measured SemiAgora process.",
        "observables": [
            "water contact angle",
            "organic residue proxy",
            "hydroxylation proxy",
            "oxide growth proxy",
        ],
        "cases": cases,
        "derived_metrics": {
            "dry_uv_o3_contact_angle_15_min_deg": dry["water_contact_angle_deg"][5],
            "dry_uv_o3_organic_removal_15_min_percent": round(100 - dry["organic_residue_proxy_percent"][5], 1),
            "dry_uv_o3_oxide_proxy_15_min_nm": dry["oxide_growth_proxy_nm"][5],
            "di_o3_water_contact_angle_15_min_deg": water["water_contact_angle_deg"][5],
        },
        "verification": {
            "cases": len(cases),
            "contact_angle_decreases_with_time": all(
                all(a >= b for a, b in zip(case["water_contact_angle_deg"], case["water_contact_angle_deg"][1:]))
                for case in cases
            ),
            "organic_residue_decreases_with_time": all(
                all(a >= b for a, b in zip(case["organic_residue_proxy_percent"], case["organic_residue_proxy_percent"][1:]))
                for case in cases
            ),
            "oxide_proxy_increases_with_time": all(
                all(a <= b for a, b in zip(case["oxide_growth_proxy_nm"], case["oxide_growth_proxy_nm"][1:]))
                for case in cases
            ),
        },
        "sources": [SOURCES["uiuc_uv_ozone"], SOURCES["samco_uv_ozone"], SOURCES["harrick"]],
        "limitations": [
            "No UV intensity distribution, ozone concentration, humidity, substrate temperature, dissolved ozone transport, or material-specific oxidation rate is modeled.",
            "The ozone-strength labels are normalized teaching axes and must not be copied into equipment settings.",
            "The oxide-growth values are proxies for caution, not measured film thickness.",
            "Real use requires material compatibility, contamination metrology, safety approval, and tool-specific training.",
        ],
    }


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


def main() -> None:
    o2 = build_o2_plasma()
    o3 = build_o3_ozone()
    assert o2["verification"]["contact_angle_decreases_with_time"]
    assert o2["verification"]["organic_residue_decreases_with_time"]
    assert o2["verification"]["risk_increases_with_time"]
    assert o3["verification"]["contact_angle_decreases_with_time"]
    assert o3["verification"]["organic_residue_decreases_with_time"]
    assert o3["verification"]["oxide_proxy_increases_with_time"]
    write_payload("process-o2-plasma-surface-activation-web-v1.json", o2)
    write_payload("process-o3-ozone-surface-clean-web-v1.json", o3)
    print(json.dumps({"o2": o2["derived_metrics"], "o3": o3["derived_metrics"]}, indent=2))


if __name__ == "__main__":
    main()
