import hashlib
import importlib.machinery
import importlib.util
import json
import os
from pathlib import Path
import subprocess
import tarfile
import tempfile
import unittest
from unittest import mock


SCRIPT_DIR = Path(__file__).resolve().parent
RUNNER_PATH = SCRIPT_DIR / "nodedc-deploy"
BUILDER_PATH = SCRIPT_DIR / "build-engine-provider-security-catalog-artifact.mjs"
ENGINE_ROOT = SCRIPT_DIR.parent.parent.parent / "NODEDC_ENGINE_INFRA"
CATALOG_REL = "nodedc-source/server/assets/provider-packages/v1/catalog.json"


def load_runner():
    loader = importlib.machinery.SourceFileLoader("nodedc_engine_provider_catalog", str(RUNNER_PATH))
    spec = importlib.util.spec_from_loader(loader.name, loader)
    module = importlib.util.module_from_spec(spec)
    loader.exec_module(module)
    return module


RUNNER = load_runner()


class EngineProviderSecurityCatalogTest(unittest.TestCase):
    def test_builder_emits_exact_backend_only_unit_profile_authority_slice(self):
        source = ENGINE_ROOT / CATALOG_REL
        if (
            not source.is_file()
            or hashlib.sha256(source.read_bytes()).hexdigest()
            != "773335bb616a5c03eb2108c4f53ef092c02e75ee75f014511201bc12e2956b27"
        ):
            self.skipTest("provider security catalog source has advanced")
        with tempfile.TemporaryDirectory(prefix="nodedc-engine-provider-catalog-") as directory:
            artifact_dir = Path(directory) / "artifacts"
            env = os.environ.copy()
            env["NODEDC_ENGINE_SOURCE_ROOT"] = str(ENGINE_ROOT)
            env["NODEDC_DEPLOY_ARTIFACT_DIR"] = str(artifact_dir)
            completed = subprocess.run(
                ["node", str(BUILDER_PATH), "20991231-999"],
                check=True,
                capture_output=True,
                text=True,
                env=env,
            )
            result = json.loads(completed.stdout)
            artifact = Path(result["artifact"])
            self.assertEqual(result["services"], ["nodedc-backend"])
            with tarfile.open(artifact, "r:gz") as archive:
                archive.extractall(Path(directory) / "extract", filter="data")
            root = Path(directory) / "extract"
            entries = tuple((root / "files.txt").read_text(encoding="utf-8").splitlines())
            payload = root / "payload"
            self.assertTrue(RUNNER.is_engine_provider_security_catalog_slice("engine", entries))
            self.assertEqual(RUNNER.component_services("engine", entries), ("nodedc-backend",))
            self.assertEqual(
                RUNNER.component_healthchecks("engine", entries, ("nodedc-backend",)),
                ("http://127.0.0.1:3001/health",),
            )
            catalog = RUNNER.validate_engine_provider_security_catalog_payload(payload, entries)
            provider = next(
                item for item in catalog["packages"] if item["id"] == "gelios.provider.v8"
            )
            previous_provider = next(
                item for item in catalog["packages"] if item["id"] == "gelios.provider.v7"
            )
            self.assertEqual(result["providerPackage"], "gelios.provider.v8")
            self.assertEqual(result["providerCredential"], "ndcProviderRotatingAccessApi")
            self.assertEqual(result["dataProductId"], "fleet.units.profile.current.v1")
            self.assertEqual(provider["capabilities"][0]["dataProductIds"], [
                "fleet.units.profile.current.v1"
            ])
            self.assertEqual(previous_provider["capabilities"][0]["dataProductIds"], [
                "fleet.positions.current.v5"
            ])

    def test_preflight_requires_exact_live_predecessor_and_immutable_backend(self):
        with tempfile.TemporaryDirectory(prefix="nodedc-engine-provider-predecessor-") as directory:
            root = Path(directory)
            target = root / CATALOG_REL
            target.parent.mkdir(parents=True)
            target.write_text("historical predecessor\n", encoding="utf-8")
            with (
                mock.patch.object(RUNNER, "component_root", return_value=root),
                mock.patch.object(
                    RUNNER,
                    "sha256_file",
                    return_value=RUNNER.ENGINE_PROVIDER_SECURITY_CATALOG_PREDECESSOR_SHA256,
                ),
                mock.patch.object(
                    RUNNER,
                    "preflight_engine_credential_backend_runtime",
                    return_value={"mode": "verified-derived-retry"},
                ),
            ):
                result = RUNNER.preflight_engine_provider_security_catalog_predecessor()
            self.assertEqual(
                result["catalog_sha256"],
                RUNNER.ENGINE_PROVIDER_SECURITY_CATALOG_PREDECESSOR_SHA256,
            )


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