#!/usr/bin/env python3
"""Validate an endpoint evidence folder and create a retained bundle."""

from __future__ import annotations

import argparse
import importlib.util
import json
import re
import sys
from pathlib import Path
from typing import Any


ROOT = Path(__file__).resolve().parents[1]
TOOL_NAME = "legacy-windows-lab-intake"
CHECKER_PATH = ROOT / "scripts" / "legacy-windows-lab-evidence-check.py"
BUNDLER_PATH = ROOT / "scripts" / "legacy-windows-first-iteration-evidence-bundle.py"


def _load_module(name: str, path: Path) -> Any:
    spec = importlib.util.spec_from_file_location(name, path)
    if spec is None or spec.loader is None:
        raise RuntimeError(f"cannot load {name}: {path}")
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module


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 _classify_legacy_os(smoke: dict[str, Any]) -> str:
    hay = " ".join(
        str(smoke.get(key) or "").lower()
        for key in ("os_caption", "os_version", "os_service_pack", "service_pack")
    )
    is_sp1 = "service pack 1" in hay or "sp1" in hay or "6.1.7601" in hay
    if "server 2008 r2" in hay and is_sp1:
        return "server_2008_r2"
    if ("windows 7" in hay or "windows nt 6.1" in hay) and is_sp1:
        return "windows_7_sp1"
    return "unknown"


def _safe_hostname(raw: str) -> str:
    value = re.sub(r"[^A-Za-z0-9_.-]+", "-", raw.strip())
    value = value.strip(".-")
    return value or "legacy-windows-host"


def _validate_inputs(args: argparse.Namespace) -> tuple[dict[str, Any] | None, str, list[str]]:
    errors: list[str] = []
    collect_root = args.collect_root
    if not collect_root.exists() or not collect_root.is_dir():
        return None, "unknown", [f"collector root is not a directory: {collect_root}"]

    smoke_path = collect_root / "legacy-windows-lab-smoke.json"
    manifest_path = collect_root / "legacy-windows-lab-collector-manifest.json"
    status_path = collect_root / "win10_upgrade.status.json"
    log_path = collect_root / "win10_upgrade.log"
    probe_path = args.win10_probe or (collect_root / "win10-probe.json")
    for role, path in (
        ("lab smoke", smoke_path),
        ("collector manifest", manifest_path),
        ("build manifest", args.build_manifest),
    ):
        if not path.exists() or not path.is_file() or path.stat().st_size <= 0:
            errors.append(f"missing required {role}: {path}")

    smoke, smoke_error = _load_json(smoke_path)
    if smoke_error:
        errors.append(smoke_error)
        smoke = None
    os_kind = _classify_legacy_os(smoke or {})
    if os_kind == "unknown":
        errors.append("evidence folder is not Windows 7 SP1 or Windows Server 2008 R2 SP1")

    bridge_present = status_path.exists() or log_path.exists()
    probe_present = probe_path.exists()
    if os_kind == "windows_7_sp1" and not (status_path.exists() and log_path.exists()):
        errors.append("Windows 7 SP1 intake requires win10_upgrade.status.json and win10_upgrade.log")
    if os_kind == "windows_7_sp1" and not probe_path.exists():
        errors.append("Windows 7 SP1 intake requires win10-probe.json")
    if os_kind == "server_2008_r2" and bridge_present:
        errors.append("Windows Server 2008 R2 SP1 intake must not include Windows 10 bridge artifacts")
    if os_kind == "server_2008_r2" and probe_present:
        errors.append("Windows Server 2008 R2 SP1 intake must not include Windows 10 probe evidence")

    return smoke, os_kind, errors


def intake(args: argparse.Namespace) -> tuple[Path | None, list[str]]:
    smoke, os_kind, errors = _validate_inputs(args)
    if errors:
        return None, errors
    assert smoke is not None

    collect_root = args.collect_root
    smoke_path = collect_root / "legacy-windows-lab-smoke.json"
    manifest_path = collect_root / "legacy-windows-lab-collector-manifest.json"
    status_path = collect_root / "win10_upgrade.status.json"
    log_path = collect_root / "win10_upgrade.log"
    probe_path = args.win10_probe or (collect_root / "win10-probe.json")
    if probe_path is not None and not probe_path.exists():
        probe_path = None

    checker = _load_module("legacy_windows_lab_evidence_check", CHECKER_PATH)
    require_win10 = os_kind == "windows_7_sp1"
    errors.extend(checker.validate(smoke_path, True, require_win10))
    errors.extend(checker.validate_collect_manifest(manifest_path))
    errors.extend(checker.validate_collect_build_manifest(manifest_path, args.build_manifest))
    errors.extend(checker.validate_smoke_collect_hostname(smoke_path, manifest_path))
    errors.extend(checker.validate_collect_root(manifest_path, collect_root))
    errors.extend(checker.validate_collect_root_smoke(smoke_path, collect_root))
    if require_win10:
        errors.extend(checker.validate_win10_bridge_artifacts(status_path, log_path))
    if errors:
        return None, errors

    hostname = _safe_hostname(args.hostname or str(smoke.get("hostname") or ""))
    output = args.output or (args.output_dir / f"legacy-windows-{hostname}-evidence.zip")
    output.parent.mkdir(parents=True, exist_ok=True)

    bundler = _load_module("legacy_windows_first_iteration_evidence_bundle", BUNDLER_PATH)
    bundle_args = argparse.Namespace(
        hostname=hostname,
        output=output,
        lab_smoke=smoke_path,
        collect_manifest=manifest_path,
        collect_root=collect_root,
        build_manifest=args.build_manifest,
        win10_upgrade_status=status_path if require_win10 else None,
        win10_upgrade_log=log_path if require_win10 else None,
        win10_probe=probe_path if require_win10 else None,
        command_history=args.command_history,
        note=args.note or [],
    )
    bundle_errors = bundler.build_bundle(bundle_args)
    if bundle_errors:
        return None, bundle_errors
    return output, []


def main(argv: list[str]) -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--collect-root", required=True, type=Path, help="Endpoint legacy-windows-lab-evidence-* folder")
    parser.add_argument("--build-manifest", required=True, type=Path, help="Mac-Studio build manifest from bin/")
    parser.add_argument("--output-dir", type=Path, default=Path("."), help="Directory for legacy-windows-<host>-evidence.zip")
    parser.add_argument("--output", type=Path, help="Explicit output ZIP path")
    parser.add_argument("--hostname", help="Override hostname for the retained evidence bundle")
    parser.add_argument("--win10-probe", type=Path, help="Optional retained __WIN10_PROBE__ output")
    parser.add_argument("--command-history", type=Path, help="Optional retained RMM command history export")
    parser.add_argument("--note", action="append", default=[], help="Operator note to include in the evidence bundle")
    args = parser.parse_args(argv)

    output, errors = intake(args)
    if errors:
        print("legacy Windows lab intake failed", file=sys.stderr)
        for error in errors:
            print(f"- {error}", file=sys.stderr)
        return 2

    print(f"legacy Windows lab intake wrote: {output}")
    return 0


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