#!/usr/bin/env python3
"""Write retained customer-pilot approval evidence for legacy Windows."""

from __future__ import annotations

import argparse
from datetime import datetime, timezone
import hashlib
import json
import sys
from pathlib import Path
from typing import Any


TOOL_NAME = "legacy-windows-first-iteration-pilot-approval"
CHECKLIST_SCHEMA = "legacy-windows-first-iteration-pilot-checklist"
FINAL_SUMMARY_SCHEMA = "legacy-windows-first-iteration-final-summary"
APPROVAL_SCHEMA = "legacy-windows-first-iteration-pilot-approval"
APPROVAL_NAME = "legacy-windows-first-iteration-pilot-approval.json"
REQUIRED_ITEM_KEYS = {
    "windows_7_sp1_lab_evidence",
    "server_2008_r2_sp1_lab_evidence",
    "final_summary_hash_sidecar",
    "win10_bridge_media_url_redacted",
    "customer_machine_selected",
    "customer_scoped_enablement_approved",
    "backup_or_image_rollback_confirmed",
    "vendor_software_reviewed",
    "clinical_equipment_reviewed",
    "customer_approval_recorded",
}
PRE_APPROVAL_PASSED_ITEM_KEYS = {
    "windows_7_sp1_lab_evidence",
    "server_2008_r2_sp1_lab_evidence",
    "final_summary_hash_sidecar",
    "win10_bridge_media_url_redacted",
}
PRE_APPROVAL_PENDING_ITEM_KEYS = REQUIRED_ITEM_KEYS - PRE_APPROVAL_PASSED_ITEM_KEYS
LAB_EVIDENCE_ITEM_KEYS = {
    "windows_7_sp1_lab_evidence",
    "server_2008_r2_sp1_lab_evidence",
    "win10_bridge_media_url_redacted",
}
REQUIRED_PRE_APPROVAL_EVIDENCE_ITEM_KEYS = LAB_EVIDENCE_ITEM_KEYS | {
    "final_summary_hash_sidecar",
}
REQUIRED_FINAL_SMOKE_CHECKS = {
    "peer_recover",
    "process:sk-agent-legacy",
    "process:sk-tray",
    "recover_health",
    "self_rescue",
    "watchdog.log",
}
PLACEHOLDER_APPROVAL_EVIDENCE = {
    "na",
    "none",
    "pending",
    "placeholder",
    "tbd",
    "todo",
    "unknown",
}
APPROVAL_FIELD_LABELS = {
    "approval_ticket": "approval ticket",
    "backup_evidence": "backup evidence",
    "clinical_equipment_review": "clinical equipment review",
    "customer": "customer",
    "customer_approval": "customer approval",
    "customer_scoped_enablement": "customer-scoped enablement evidence",
    "operator": "operator",
    "pilot_hostname": "pilot hostname",
    "rollback_plan": "rollback plan",
    "vendor_software_review": "vendor software review",
}


def _utc_timestamp() -> str:
    return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")


def _sha256(path: Path) -> str:
    h = hashlib.sha256()
    with path.open("rb") as f:
        for chunk in iter(lambda: f.read(1024 * 1024), b""):
            h.update(chunk)
    return h.hexdigest()


def _is_sha256_hex(value: str) -> bool:
    if len(value) != 64:
        return False
    try:
        int(value, 16)
    except ValueError:
        return False
    return True


def _load_json(path: Path) -> tuple[dict[str, Any] | None, str | None]:
    try:
        payload = json.loads(path.read_text(encoding="utf-8"))
    except Exception as exc:
        return None, f"{path}: cannot read JSON: {exc}"
    if not isinstance(payload, dict):
        return None, f"{path}: root JSON value must be an object"
    return payload, None


def _validate_sidecar(path: Path, label: str) -> list[str]:
    sidecar = path.with_suffix(path.suffix + ".sha256")
    if not sidecar.exists():
        return [f"{label} checksum sidecar is missing: {sidecar}"]
    try:
        parts = sidecar.read_text(encoding="utf-8").strip().split()
    except Exception as exc:
        return [f"{label} checksum sidecar cannot be read: {exc}"]
    if not parts:
        return [f"{label} checksum sidecar cannot be read: empty sidecar"]
    expected = parts[0]
    if len(parts) < 2:
        return [f"{label} checksum sidecar filename is missing"]
    if len(parts) > 2:
        return [f"{label} checksum sidecar must contain exactly sha256 and filename"]
    if parts[1] != path.name:
        return [f"{label} checksum sidecar filename mismatch"]
    if not _is_sha256_hex(expected):
        return [f"{label} checksum sidecar SHA-256 is malformed"]
    actual = _sha256(path)
    if expected != actual:
        return [f"{label} checksum sidecar mismatch"]
    return []


def _resolve_recorded_path(recorded: str, base: Path) -> Path:
    path = Path(recorded)
    if path.is_absolute():
        return path
    return base / path


def _nonempty(value: str | None) -> str:
    return str(value or "").strip()


def _is_placeholder_approval_evidence(value: str) -> bool:
    text = value.strip().lower()
    normalized = "".join(char for char in text if char.isalnum())
    return text in PLACEHOLDER_APPROVAL_EVIDENCE or normalized in PLACEHOLDER_APPROVAL_EVIDENCE


def _same_windows_hostname(left: str, right: str) -> bool:
    return left.strip().lower() == right.strip().lower()


def _is_windows_computer_name(value: str) -> bool:
    text = value.strip()
    if len(text) < 1 or len(text) > 15:
        return False
    if text[0] == "-" or text[-1] == "-":
        return False
    return all(
        ("A" <= char <= "Z")
        or ("a" <= char <= "z")
        or ("0" <= char <= "9")
        or char == "-"
        for char in text
    )


def _duplicate_item_keys(items: list[Any]) -> list[str]:
    seen: set[str] = set()
    duplicates: set[str] = set()
    for item in items:
        if not isinstance(item, dict):
            continue
        key = str(item.get("key") or "")
        if not key:
            continue
        if key in seen:
            duplicates.add(key)
        seen.add(key)
    return sorted(duplicates)


def _unknown_item_keys(items: list[Any]) -> list[str]:
    keys = {
        str(item.get("key") or "")
        for item in items
        if isinstance(item, dict) and str(item.get("key") or "")
    }
    return sorted(keys - REQUIRED_ITEM_KEYS)


def _validate_checklist_item_entries(path: Path, items: list[Any]) -> list[str]:
    if any(not isinstance(item, dict) for item in items):
        return [f"{path.name}: checklist item entries must be objects"]
    return []


def _validate_checklist_item_keys(path: Path, items: list[Any]) -> list[str]:
    errors: list[str] = []
    for item in items:
        if not isinstance(item, dict):
            continue
        if "key" not in item or item.get("key") is None:
            errors.append(f"{path.name}: checklist item key is required")
            continue
        key = item.get("key")
        if not isinstance(key, str):
            errors.append(f"{path.name}: checklist item key must be a string")
        elif not key.strip():
            errors.append(f"{path.name}: checklist item key is required")
    return errors


def _validate_checklist_item_status_types(path: Path, items: list[Any]) -> list[str]:
    errors: list[str] = []
    for item in items:
        if not isinstance(item, dict):
            continue
        key = str(item.get("key") or "")
        if "status" in item and not isinstance(item.get("status"), str):
            errors.append(f"{path.name}: checklist item {key} status must be a string")
    return errors


def _validate_checklist_item_evidence_types(path: Path, items: list[Any]) -> list[str]:
    errors: list[str] = []
    for item in items:
        if not isinstance(item, dict):
            continue
        key = str(item.get("key") or "")
        if "evidence" in item and not isinstance(item.get("evidence"), str):
            errors.append(f"{path.name}: checklist item {key} evidence must be a string")
    return errors


def _validate_required_lab_evidence(path: Path, items: list[Any]) -> list[str]:
    errors: list[str] = []
    for item in items:
        if not isinstance(item, dict):
            continue
        key = str(item.get("key") or "")
        if key not in REQUIRED_PRE_APPROVAL_EVIDENCE_ITEM_KEYS:
            continue
        evidence = item.get("evidence")
        if evidence is None or (isinstance(evidence, str) and not evidence.strip()):
            errors.append(f"{path.name}: checklist item {key} evidence is required")
    return errors


def _validate_pending_checklist_item_evidence(path: Path, items: list[Any]) -> list[str]:
    errors: list[str] = []
    for item in items:
        if not isinstance(item, dict):
            continue
        key = str(item.get("key") or "")
        if key not in PRE_APPROVAL_PENDING_ITEM_KEYS:
            continue
        evidence = item.get("evidence")
        if isinstance(evidence, str) and evidence.strip():
            errors.append(f"{path.name}: checklist item {key} evidence must be empty before approval")
    return errors


def _final_summary_host_by_platform(summary: dict[str, Any]) -> dict[str, str]:
    hosts: dict[str, str] = {}
    platforms = summary.get("platforms")
    if not isinstance(platforms, list):
        return hosts
    for platform in platforms:
        if not isinstance(platform, dict):
            continue
        os_kind = platform.get("os_kind")
        hostname = platform.get("hostname")
        if isinstance(os_kind, str) and isinstance(hostname, str):
            hosts[os_kind.strip()] = hostname.strip()
    return hosts


def _validate_lab_evidence_matches_final_summary(
    path: Path,
    items: list[Any],
    summary: dict[str, Any],
) -> list[str]:
    errors: list[str] = []
    host_by_platform = _final_summary_host_by_platform(summary)
    expected_platforms = {
        "windows_7_sp1_lab_evidence": "windows_7_sp1",
        "server_2008_r2_sp1_lab_evidence": "server_2008_r2",
        "win10_bridge_media_url_redacted": "windows_7_sp1",
    }
    for item in items:
        if not isinstance(item, dict):
            continue
        key = str(item.get("key") or "")
        os_kind = expected_platforms.get(key)
        if os_kind is None:
            continue
        evidence = item.get("evidence")
        if not isinstance(evidence, str):
            continue
        if evidence.strip() != host_by_platform.get(os_kind, ""):
            errors.append(
                f"{path.name}: checklist item {key} evidence must match final summary {os_kind} hostname"
            )
    return errors


def _validate_final_summary_sidecar_evidence(
    path: Path,
    items: list[Any],
    final_summary: Path,
) -> list[str]:
    errors: list[str] = []
    expected = final_summary.with_suffix(final_summary.suffix + ".sha256").resolve(strict=False)
    for item in items:
        if not isinstance(item, dict):
            continue
        if str(item.get("key") or "") != "final_summary_hash_sidecar":
            continue
        evidence = item.get("evidence")
        if not isinstance(evidence, str) or not evidence.strip():
            continue
        actual = _resolve_recorded_path(evidence.strip(), path.parent).resolve(strict=False)
        if actual != expected:
            errors.append(
                f"{path.name}: checklist item final_summary_hash_sidecar evidence must point to final summary checksum sidecar"
            )
    return errors


def _validate_args(args: argparse.Namespace) -> list[str]:
    required = {
        "customer": "customer is required",
        "pilot_hostname": "pilot hostname is required",
        "operator": "operator is required",
        "approval_ticket": "approval ticket is required",
        "customer_scoped_enablement": "customer-scoped enablement evidence is required",
        "backup_evidence": "backup evidence is required",
        "rollback_plan": "rollback plan is required",
        "vendor_software_review": "vendor software review is required",
        "clinical_equipment_review": "clinical equipment review is required",
        "customer_approval": "customer approval is required",
    }
    errors = []
    for attr, message in required.items():
        value = _nonempty(getattr(args, attr, ""))
        if not value:
            errors.append(message)
        elif attr in APPROVAL_FIELD_LABELS and _is_placeholder_approval_evidence(value):
            errors.append(f"{APPROVAL_FIELD_LABELS[attr]} cannot be placeholder approval evidence")
    pilot_hostname = _nonempty(getattr(args, "pilot_hostname", ""))
    if pilot_hostname and not _is_windows_computer_name(pilot_hostname):
        errors.append(
            "pilot hostname must be a valid Windows computer name using ASCII letters, numbers, or hyphens"
        )
    return errors


def _validate_final_summary_build_manifest(path: Path, summary: dict[str, Any]) -> list[str]:
    errors: list[str] = []
    build_manifest_text = str(summary.get("build_manifest") or "").strip()
    if not build_manifest_text:
        return [f"{path.name}: build_manifest is required"]
    build_manifest = _resolve_recorded_path(build_manifest_text, path.parent)
    if not build_manifest.exists():
        return [f"{path.name}: build_manifest is missing: {build_manifest}"]
    expected_sha = str(summary.get("build_manifest_sha256") or "").strip()
    if not expected_sha:
        errors.append(f"{path.name}: build_manifest_sha256 is required")
    elif not _is_sha256_hex(expected_sha):
        errors.append(f"{path.name}: build_manifest_sha256 is malformed")
    elif expected_sha != _sha256(build_manifest):
        errors.append(f"{path.name}: build_manifest_sha256 mismatch")
    raw_build_manifest_size = summary.get("build_manifest_size_bytes")
    expected_size = (
        raw_build_manifest_size
        if isinstance(raw_build_manifest_size, int) and not isinstance(raw_build_manifest_size, bool)
        else 0
    )
    if expected_size <= 0:
        errors.append(f"{path.name}: build_manifest_size_bytes is required")
    elif expected_size != build_manifest.stat().st_size:
        errors.append(f"{path.name}: build_manifest_size_bytes mismatch")
    return errors


def _validate_final_summary_status(path: Path, summary: dict[str, Any]) -> list[str]:
    errors: list[str] = []
    if summary.get("status") != "passed":
        errors.append(f"{path.name}: status must be passed")
    if summary.get("readiness") != "legacy Windows first-iteration readiness passed":
        errors.append(f"{path.name}: readiness must be legacy Windows first-iteration readiness passed")
    return errors


def _validate_final_summary_platform_hostnames(path: Path, summary: dict[str, Any]) -> list[str]:
    errors: list[str] = []
    platforms = summary.get("platforms")
    if not isinstance(platforms, list):
        return [f"{path.name}: platforms must be a list"]
    platform_counts: dict[str, int] = {}
    platform_hostnames: list[str] = []
    for platform in platforms:
        if not isinstance(platform, dict):
            errors.append(f"{path.name}: platform entries must be objects")
            continue
        raw_os_kind = platform.get("os_kind")
        if not isinstance(raw_os_kind, str):
            errors.append(f"{path.name}: platform os_kind must be a string")
            continue
        os_kind = raw_os_kind.strip()
        if os_kind not in {"windows_7_sp1", "server_2008_r2"}:
            errors.append(f"{path.name}: unknown platform os_kind: {os_kind or '<empty>'}")
            continue
        platform_counts[os_kind] = platform_counts.get(os_kind, 0) + 1
        raw_hostname = platform.get("hostname")
        if not isinstance(raw_hostname, str):
            errors.append(f"{os_kind}: final summary platform hostname must be a string")
        elif not raw_hostname.strip():
            errors.append(f"{os_kind}: final summary platform hostname is required")
        else:
            hostname = raw_hostname.strip()
            if any(_same_windows_hostname(hostname, existing) for existing in platform_hostnames):
                errors.append(f"{path.name}: duplicate retained lab hostname: {hostname}")
            platform_hostnames.append(hostname)
    for os_kind, missing_message, duplicate_message in (
        (
            "windows_7_sp1",
            "missing Windows 7 SP1 platform proof",
            "duplicate Windows 7 SP1 platform proof",
        ),
        (
            "server_2008_r2",
            "missing Windows Server 2008 R2 SP1 platform proof",
            "duplicate Windows Server 2008 R2 SP1 platform proof",
        ),
    ):
        count = platform_counts.get(os_kind, 0)
        if count == 0:
            errors.append(f"{path.name}: {missing_message}")
        elif count > 1:
            errors.append(f"{path.name}: {duplicate_message}")
    return errors


def _validate_final_summary_platform_bundle_proofs(path: Path, summary: dict[str, Any]) -> list[str]:
    errors: list[str] = []
    platforms = summary.get("platforms")
    if not isinstance(platforms, list):
        return []
    for platform in platforms:
        if not isinstance(platform, dict):
            continue
        os_kind = str(platform.get("os_kind") or "").strip()
        raw_hostname = platform.get("hostname")
        hostname = raw_hostname.strip() if isinstance(raw_hostname, str) and raw_hostname.strip() else os_kind
        bundle_text = str(platform.get("bundle") or "").strip()
        if not bundle_text:
            errors.append(f"{hostname}: final summary platform bundle is required")
            continue
        bundle = _resolve_recorded_path(bundle_text, path.parent)
        if not bundle.exists():
            errors.append(f"{hostname}: final summary platform bundle is missing: {bundle}")
            continue
        expected_sha = str(platform.get("sha256") or "").strip()
        if not expected_sha:
            errors.append(f"{hostname}: final summary platform sha256 is required")
        elif not _is_sha256_hex(expected_sha):
            errors.append(f"{hostname}: final summary platform sha256 is malformed")
        elif expected_sha != _sha256(bundle):
            errors.append(f"{hostname}: final summary platform sha256 mismatch")
        raw_size = platform.get("size_bytes")
        expected_size = raw_size if isinstance(raw_size, int) and not isinstance(raw_size, bool) else 0
        if expected_size <= 0:
            errors.append(f"{hostname}: final summary platform size_bytes is required")
        elif expected_size != bundle.stat().st_size:
            errors.append(f"{hostname}: final summary platform size_bytes mismatch")
    return errors


def _validate_final_summary_collector_proofs(path: Path, summary: dict[str, Any]) -> list[str]:
    errors: list[str] = []
    platforms = summary.get("platforms")
    if not isinstance(platforms, list):
        return []
    expected_build_manifest_sha = str(summary.get("build_manifest_sha256") or "").strip()
    for platform in platforms:
        if not isinstance(platform, dict):
            continue
        os_kind = str(platform.get("os_kind") or "").strip()
        raw_hostname = platform.get("hostname")
        hostname = raw_hostname.strip() if isinstance(raw_hostname, str) and raw_hostname.strip() else os_kind
        collector = platform.get("collector")
        if not isinstance(collector, dict):
            errors.append(f"{hostname}: final summary platform collector proof is required")
            collector = {}
        for key in ("collector_exit", "smoke_exit"):
            value = collector.get(key)
            if isinstance(value, bool) or not isinstance(value, int) or value != 0:
                errors.append(f"{hostname}: final summary collector {key} must be 0")
        raw_files_count = collector.get("files_count")
        if isinstance(raw_files_count, bool) or not isinstance(raw_files_count, int) or raw_files_count <= 0:
            errors.append(f"{hostname}: final summary collector files_count is required")
        collector_build_manifest_sha = str(collector.get("build_manifest_sha256") or "").strip()
        if not collector_build_manifest_sha:
            errors.append(f"{hostname}: final summary collector build_manifest_sha256 is required")
        elif not _is_sha256_hex(collector_build_manifest_sha):
            errors.append(f"{hostname}: final summary collector build_manifest_sha256 is malformed")
        elif expected_build_manifest_sha and collector_build_manifest_sha != expected_build_manifest_sha:
            errors.append(f"{hostname}: final summary collector build_manifest_sha256 mismatch")
    return errors


def _validate_final_summary_operator_context_proofs(path: Path, summary: dict[str, Any]) -> list[str]:
    errors: list[str] = []
    platforms = summary.get("platforms")
    if not isinstance(platforms, list):
        return []
    for platform in platforms:
        if not isinstance(platform, dict):
            continue
        os_kind = str(platform.get("os_kind") or "").strip()
        raw_hostname = platform.get("hostname")
        hostname = raw_hostname.strip() if isinstance(raw_hostname, str) and raw_hostname.strip() else os_kind
        operator_context = platform.get("operator_context")
        if not isinstance(operator_context, dict):
            errors.append(f"{hostname}: final summary operator_context proof is required")
            operator_context = {}
        if operator_context.get("is_elevated") is not True:
            errors.append(f"{hostname}: final summary operator_context is_elevated must be true")
        raw_computer_name = operator_context.get("computer_name")
        computer_name = raw_computer_name.strip() if isinstance(raw_computer_name, str) else ""
        if not isinstance(raw_computer_name, str):
            errors.append(f"{hostname}: final summary operator_context computer_name must be a string")
        elif hostname and not _same_windows_hostname(computer_name, hostname):
            errors.append(f"{hostname}: final summary operator_context computer_name must match hostname")
    return errors


def _validate_final_summary_smoke_proofs(path: Path, summary: dict[str, Any]) -> list[str]:
    errors: list[str] = []
    platforms = summary.get("platforms")
    if not isinstance(platforms, list):
        return []
    for platform in platforms:
        if not isinstance(platform, dict):
            continue
        os_kind = str(platform.get("os_kind") or "").strip()
        raw_hostname = platform.get("hostname")
        hostname = raw_hostname.strip() if isinstance(raw_hostname, str) and raw_hostname.strip() else os_kind
        smoke_checks = platform.get("smoke_checks")
        if not isinstance(smoke_checks, dict):
            errors.append(f"{hostname}: final summary smoke_checks proof is required")
            smoke_checks = {}
        for check in sorted(REQUIRED_FINAL_SMOKE_CHECKS):
            if smoke_checks.get(check) is not True:
                errors.append(f"{hostname}: final summary smoke check {check} must be true")
    return errors


def _validate_final_summary_win10_proofs(path: Path, summary: dict[str, Any]) -> list[str]:
    errors: list[str] = []
    platforms = summary.get("platforms")
    if not isinstance(platforms, list):
        return []
    for platform in platforms:
        if not isinstance(platform, dict):
            continue
        os_kind = str(platform.get("os_kind") or "").strip()
        raw_hostname = platform.get("hostname")
        hostname = raw_hostname.strip() if isinstance(raw_hostname, str) and raw_hostname.strip() else os_kind
        win10_probe = platform.get("win10_probe")
        win10_bridge = platform.get("win10_bridge")
        if os_kind == "windows_7_sp1":
            if not isinstance(win10_probe, dict) or win10_probe.get("verdict") != "candidate":
                errors.append(f"{hostname}: final summary win10_probe verdict must be candidate")
            if not isinstance(win10_bridge, dict):
                errors.append(f"{hostname}: final summary win10_bridge proof is required")
            else:
                if win10_bridge.get("acknowledgements_recorded") is not True:
                    errors.append(
                        f"{hostname}: final summary win10_bridge acknowledgements_recorded must be true"
                    )
                if win10_bridge.get("evidence_notes_recorded") is not True:
                    errors.append(
                        f"{hostname}: final summary win10_bridge evidence_notes_recorded must be true"
                    )
                if win10_bridge.get("media_url_query_redacted") is not True:
                    errors.append(
                        f"{hostname}: final summary win10_bridge media_url_query_redacted must be true"
                    )
                if win10_bridge.get("probe_verdict") != "candidate":
                    errors.append(f"{hostname}: final summary win10_bridge probe_verdict must be candidate")
                if win10_bridge.get("setup_launched") is not True:
                    errors.append(f"{hostname}: final summary win10_bridge setup_launched must be true")
        elif os_kind == "server_2008_r2":
            if win10_probe not in ({}, None):
                errors.append(f"{hostname}: final summary Server 2008 R2 win10_probe must be empty")
            if win10_bridge not in ({}, None):
                errors.append(f"{hostname}: final summary Server 2008 R2 win10_bridge must be empty")
    return errors


def _validate_checklist(path: Path) -> tuple[dict[str, Any] | None, dict[str, Any] | None, list[str]]:
    errors = _validate_sidecar(path, "pilot checklist")
    checklist, error = _load_json(path)
    if error:
        errors.append(error)
        return None, None, errors
    assert checklist is not None
    if checklist.get("schema") != CHECKLIST_SCHEMA:
        errors.append(f"{path.name}: schema is not {CHECKLIST_SCHEMA}")
    if checklist.get("status") != "lab_passed_customer_approval_required":
        errors.append(f"{path.name}: status must be lab_passed_customer_approval_required")
    if checklist.get("customer_pilot_ready") is not False:
        errors.append(f"{path.name}: customer_pilot_ready must be false before approval")

    items = checklist.get("items")
    if not isinstance(items, list):
        errors.append(f"{path.name}: items must be a list")
        item_keys = set()
        item_statuses: dict[str, str] = {}
    else:
        errors.extend(_validate_checklist_item_entries(path, items))
        errors.extend(_validate_checklist_item_keys(path, items))
        duplicates = _duplicate_item_keys(items)
        if duplicates:
            errors.append(f"{path.name}: duplicate pilot checklist item(s): {', '.join(duplicates)}")
        unknown = _unknown_item_keys(items)
        if unknown:
            errors.append(f"{path.name}: unknown pilot checklist item(s): {', '.join(unknown)}")
        errors.extend(_validate_checklist_item_status_types(path, items))
        errors.extend(_validate_checklist_item_evidence_types(path, items))
        errors.extend(_validate_required_lab_evidence(path, items))
        errors.extend(_validate_pending_checklist_item_evidence(path, items))
        item_keys = {
            str(item.get("key") or "")
            for item in items
            if isinstance(item, dict)
        }
        item_statuses = {
            str(item.get("key") or ""): str(item.get("status") or "")
            for item in items
            if isinstance(item, dict)
        }
    missing = sorted(REQUIRED_ITEM_KEYS - item_keys)
    if missing:
        errors.append(f"{path.name}: missing pilot checklist item(s): {', '.join(missing)}")
    for key in sorted(PRE_APPROVAL_PASSED_ITEM_KEYS):
        if item_statuses.get(key) != "passed":
            errors.append(f"{path.name}: checklist item {key} must be passed")
    for key in sorted(PRE_APPROVAL_PENDING_ITEM_KEYS):
        if item_statuses.get(key) != "pending":
            errors.append(f"{path.name}: checklist item {key} must be pending before approval")

    summary = None
    final_summary_text = str(checklist.get("final_summary") or "").strip()
    if not final_summary_text:
        errors.append(f"{path.name}: final_summary is required")
    else:
        final_summary = _resolve_recorded_path(final_summary_text, path.parent)
    if final_summary_text and not final_summary.exists():
        errors.append(f"{path.name}: final_summary is missing: {final_summary}")
    elif final_summary_text:
        errors.extend(_validate_sidecar(final_summary, "final summary"))
        summary, summary_error = _load_json(final_summary)
        if summary_error:
            errors.append(summary_error)
            summary = None
        if summary is not None:
            if summary.get("schema") != FINAL_SUMMARY_SCHEMA:
                errors.append(f"{final_summary.name}: schema is not {FINAL_SUMMARY_SCHEMA}")
            errors.extend(_validate_final_summary_status(final_summary, summary))
            errors.extend(_validate_final_summary_build_manifest(final_summary, summary))
            errors.extend(_validate_final_summary_platform_hostnames(final_summary, summary))
            errors.extend(_validate_final_summary_platform_bundle_proofs(final_summary, summary))
            errors.extend(_validate_final_summary_collector_proofs(final_summary, summary))
            errors.extend(_validate_final_summary_operator_context_proofs(final_summary, summary))
            errors.extend(_validate_final_summary_smoke_proofs(final_summary, summary))
            errors.extend(_validate_final_summary_win10_proofs(final_summary, summary))
            if isinstance(items, list):
                errors.extend(_validate_lab_evidence_matches_final_summary(path, items, summary))
                errors.extend(_validate_final_summary_sidecar_evidence(path, items, final_summary))
            checklist_build_manifest_sha = str(checklist.get("build_manifest_sha256") or "").strip()
            if not checklist_build_manifest_sha:
                errors.append(f"{path.name}: build_manifest_sha256 is required")
            elif not _is_sha256_hex(checklist_build_manifest_sha):
                errors.append(f"{path.name}: build_manifest_sha256 is malformed")
            elif checklist_build_manifest_sha != summary.get("build_manifest_sha256"):
                errors.append(f"{path.name}: build_manifest_sha256 mismatch")
        expected = str(checklist.get("final_summary_sha256") or "")
        actual = _sha256(final_summary)
        if not expected:
            errors.append(f"{path.name}: final_summary_sha256 is required")
        elif not _is_sha256_hex(expected):
            errors.append(f"{path.name}: final_summary_sha256 is malformed")
        elif expected != actual:
            errors.append(f"{path.name}: final_summary_sha256 mismatch")
    return checklist, summary, errors


def _lab_hosts_from_checklist(checklist: dict[str, Any]) -> list[str]:
    lab_hosts: list[str] = []
    for item in checklist.get("items") or []:
        if not isinstance(item, dict):
            continue
        if str(item.get("key") or "") not in LAB_EVIDENCE_ITEM_KEYS:
            continue
        evidence = _nonempty(item.get("evidence"))
        if evidence:
            lab_hosts.append(evidence)
    return lab_hosts


def _lab_hosts_from_final_summary(summary: dict[str, Any] | None) -> list[str]:
    if not isinstance(summary, dict):
        return []
    lab_hosts: list[str] = []
    for platform in summary.get("platforms") or []:
        if not isinstance(platform, dict):
            continue
        if str(platform.get("os_kind") or "") not in {"windows_7_sp1", "server_2008_r2"}:
            continue
        hostname = _nonempty(platform.get("hostname"))
        if hostname:
            lab_hosts.append(hostname)
    return lab_hosts


def _validate_pilot_hostname_against_lab_hosts(
    checklist: dict[str, Any],
    summary: dict[str, Any] | None,
    args: argparse.Namespace,
) -> list[str]:
    pilot_hostname = _nonempty(args.pilot_hostname).lower()
    if not pilot_hostname:
        return []
    lab_hosts = _lab_hosts_from_checklist(checklist) + _lab_hosts_from_final_summary(summary)
    for lab_host in lab_hosts:
        if pilot_hostname == lab_host.lower():
            return [f"pilot hostname {args.pilot_hostname} matches retained lab evidence host {lab_host}"]
    return []


def _approval_items(checklist: dict[str, Any], args: argparse.Namespace) -> list[dict[str, Any]]:
    evidence = {
        "customer_machine_selected": _nonempty(args.pilot_hostname),
        "customer_scoped_enablement_approved": _nonempty(args.customer_scoped_enablement),
        "backup_or_image_rollback_confirmed": _nonempty(args.backup_evidence),
        "vendor_software_reviewed": _nonempty(args.vendor_software_review),
        "clinical_equipment_reviewed": _nonempty(args.clinical_equipment_review),
        "customer_approval_recorded": _nonempty(args.customer_approval),
    }
    output: list[dict[str, Any]] = []
    for item in checklist.get("items") or []:
        if not isinstance(item, dict):
            continue
        key = str(item.get("key") or "")
        approved = dict(item)
        approved["status"] = "passed"
        if key in evidence:
            approved["evidence"] = evidence[key]
        output.append(approved)
    return output


def approve(args: argparse.Namespace) -> tuple[Path | None, list[str]]:
    errors = _validate_args(args)
    checklist, summary, checklist_errors = _validate_checklist(args.checklist)
    errors.extend(checklist_errors)
    if checklist is not None:
        errors.extend(_validate_pilot_hostname_against_lab_hosts(checklist, summary, args))
    if errors:
        return None, errors
    assert checklist is not None

    args.output_dir.mkdir(parents=True, exist_ok=True)
    output_path = args.output_dir / APPROVAL_NAME
    final_summary = _resolve_recorded_path(
        str(checklist.get("final_summary") or ""),
        args.checklist.parent,
    )
    payload = {
        "schema": APPROVAL_SCHEMA,
        "status": "customer_pilot_approved",
        "customer_pilot_ready": True,
        "generated_at": _utc_timestamp(),
        "source_checklist": str(args.checklist),
        "source_checklist_sha256": _sha256(args.checklist),
        "build_manifest_sha256": str(checklist.get("build_manifest_sha256") or ""),
        "final_summary": str(final_summary),
        "final_summary_sha256": _sha256(final_summary),
        "approval": {
            "approval_ticket": _nonempty(args.approval_ticket),
            "backup_evidence": _nonempty(args.backup_evidence),
            "clinical_equipment_review": _nonempty(args.clinical_equipment_review),
            "customer": _nonempty(args.customer),
            "customer_approval": _nonempty(args.customer_approval),
            "customer_scoped_enablement": _nonempty(args.customer_scoped_enablement),
            "operator": _nonempty(args.operator),
            "pilot_hostname": _nonempty(args.pilot_hostname),
            "rollback_plan": _nonempty(args.rollback_plan),
            "vendor_software_review": _nonempty(args.vendor_software_review),
        },
        "items": _approval_items(checklist, args),
    }
    output_path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
    output_path.with_suffix(output_path.suffix + ".sha256").write_text(
        f"{_sha256(output_path)}  {output_path.name}\n",
        encoding="utf-8",
    )
    return output_path, []


def main(argv: list[str]) -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--checklist", required=True, type=Path, help="legacy-windows-first-iteration-pilot-checklist.json")
    parser.add_argument("--output-dir", required=True, type=Path, help="Directory for retained pilot approval evidence")
    parser.add_argument("--customer", default="", help="Customer or site name for the one-machine pilot")
    parser.add_argument("--pilot-hostname", default="", help="Single doctor-office endpoint selected for pilot")
    parser.add_argument("--operator", default="", help="Operator recording the approval")
    parser.add_argument("--approval-ticket", default="", help="Ticket/change/request identifier")
    parser.add_argument("--customer-scoped-enablement", default="", help="Evidence that enablement is scoped to the customer/pilot machine")
    parser.add_argument("--backup-evidence", default="", help="Backup or image evidence before customer pilot")
    parser.add_argument("--rollback-plan", default="", help="Rollback or fix-forward plan")
    parser.add_argument("--vendor-software-review", default="", help="Vendor software approval or review note")
    parser.add_argument("--clinical-equipment-review", default="", help="Clinical equipment/peripheral review note")
    parser.add_argument("--customer-approval", default="", help="Explicit customer approval note")
    args = parser.parse_args(argv)

    output, errors = approve(args)
    if errors:
        print("legacy Windows first-iteration pilot approval failed", file=sys.stderr)
        for error in errors:
            print(f"- {error}", file=sys.stderr)
        return 2

    assert output is not None
    print(f"legacy Windows first-iteration pilot approval wrote: {output}")
    print(f"pilot approval checksum: {output.with_suffix(output.suffix + '.sha256')}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main(sys.argv[1:]))
