#!/usr/bin/env python3
from __future__ import annotations

import hashlib
import importlib.util
import json
import subprocess
import tempfile
import unittest
from pathlib import Path

ROOT = Path(__file__).resolve().parents[3]
MODULE_PATH = ROOT / "tools/k3s/upgrade-plan.py"
SPEC = importlib.util.spec_from_file_location("upgrade_plan", MODULE_PATH)
assert SPEC and SPEC.loader
upgrade_plan = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(upgrade_plan)


class UpgradePlanTest(unittest.TestCase):
    def test_digest_locked_plan_uses_outer_launcher_and_never_applies(self) -> None:
        with tempfile.TemporaryDirectory() as tmp:
            root = Path(tmp)
            lock = root / "version-lock.json"
            lock.write_text(json.dumps({
                "schema_version": 2,
                "managed_by": "overdeck-k3s-phase1",
                "version": "v1.32.1+k3s1",
                "launcher": {"invocation_path": "/usr/local/bin/k3s", "sha256": "a" * 64},
                "runtime": {"present": True, "sha256": "b" * 64},
            }))
            candidate = root / "k3s"
            candidate.write_text("#!/usr/bin/env bash\nprintf 'k3s version v1.36.3+k3s1 (fixture)\\n'\n")
            candidate.chmod(0o755)
            digest = hashlib.sha256(candidate.read_bytes()).hexdigest()
            plan = upgrade_plan.build_plan(lock, candidate, "v1.36.3+k3s1", digest)
            self.assertFalse(plan["mutation_performed"])
            self.assertFalse(plan["apply_supported_by_phase1"])
            self.assertEqual(plan["candidate"]["launcher_sha256"], digest)
            self.assertNotIn("runtime", plan["candidate"])

    def test_legacy_ambiguous_lock_is_rejected(self) -> None:
        with tempfile.TemporaryDirectory() as tmp:
            root = Path(tmp); lock = root / "lock.json"; lock.write_text(json.dumps({"schema_version": 1, "managed_by": "overdeck-k3s-phase1", "version": "v1", "binary_sha256": "a" * 64})); candidate = root / "k3s"; candidate.write_text("#!/bin/sh\necho 'k3s version v2 (x)'\n"); candidate.chmod(0o755)
            with self.assertRaisesRegex(upgrade_plan.Phase1Error, "separate launcher/runtime"):
                upgrade_plan.build_plan(lock, candidate, "v2", hashlib.sha256(candidate.read_bytes()).hexdigest())

    def test_shell_wrapper_refuses_apply(self) -> None:
        completed = subprocess.run([str(ROOT / "tools/k3s/upgrade-control-plane.sh"), "--apply"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=False)
        self.assertEqual(completed.returncode, 65)
        self.assertIn("refuses", completed.stderr)


if __name__ == "__main__":
    unittest.main()
