from __future__ import annotations

import copy
import importlib.util
import unittest
from pathlib import Path

import yaml


DEPLOY_ROOT = Path(__file__).resolve().parents[1]
SPEC = importlib.util.spec_from_file_location(
    "validate_config", DEPLOY_ROOT / "scripts" / "validate_config.py"
)
assert SPEC and SPEC.loader
validate_config = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(validate_config)


def load_yaml(relative_path: str) -> dict:
    return yaml.safe_load((DEPLOY_ROOT / relative_path).read_text(encoding="utf-8"))


def safe_secrets() -> dict:
    secrets = load_yaml("config/secrets.example.yaml")
    secrets["rke2"]["token"] = "r" * 48
    app = secrets["application"]
    app["postgresPassword"] = "p" * 24
    app["redisPassword"] = "r" * 24
    app["minioRootPassword"] = "m" * 24
    app["artifactInstallerToken"] = "a" * 48
    app["uavAccessInternalToken"] = "u" * 48
    app["flighthubEventCallbackToken"] = "c" * 48
    app["emqxDashboardPassword"] = "e" * 24
    return secrets


def validate(site: dict, inventory: dict, secrets: dict | None = None):
    result = validate_config.ValidationResult()
    profile = validate_config.validate_site(site, result)
    validate_config.validate_inventory(inventory, profile, site, result)
    validate_config.validate_secrets(secrets or safe_secrets(), result)
    validate_config.validate_cross_file(site, secrets or safe_secrets(), result)
    return result


class ConfigurationValidationTests(unittest.TestCase):
    def test_single_example_contract_is_valid_with_real_secrets(self):
        result = validate(
            load_yaml("config/site.example.yaml"),
            load_yaml("inventories/single/hosts.example.yml"),
        )
        self.assertEqual([], result.errors)

    def test_ha_example_contract_is_valid_with_real_secrets(self):
        result = validate(
            load_yaml("config/site-ha.example.yaml"),
            load_yaml("inventories/ha/hosts.example.yml"),
        )
        self.assertEqual([], result.errors)

    def test_rejects_overlapping_cluster_networks(self):
        site = load_yaml("config/site.example.yaml")
        site["cluster"]["serviceCidr"] = site["cluster"]["podCidr"]
        result = validate(site, load_yaml("inventories/single/hosts.example.yml"))
        self.assertTrue(any("CIDR" in error for error in result.errors))

    def test_rejects_single_profile_with_multiple_servers(self):
        inventory = load_yaml("inventories/single/hosts.example.yml")
        inventory = copy.deepcopy(inventory)
        inventory["all"]["children"]["rke2_servers"]["hosts"]["extra"] = {
            "ansible_host": "192.0.2.11",
            "rke2_node_ip": "192.0.2.11",
        }
        result = validate(load_yaml("config/site.example.yaml"), inventory)
        self.assertTrue(any("single" in error for error in result.errors))

    def test_rejects_ha_with_bundled_single_instance_data_services(self):
        site = load_yaml("config/site-ha.example.yaml")
        site["helmValues"]["bundledDataServices"]["enabled"] = True
        result = validate(site, load_yaml("inventories/ha/hosts.example.yml"))
        self.assertTrue(any("Chart" in error for error in result.errors))

    def test_rejects_placeholder_secrets(self):
        result = validate(
            load_yaml("config/site.example.yaml"),
            load_yaml("inventories/single/hosts.example.yml"),
            load_yaml("config/secrets.example.yaml"),
        )
        self.assertGreater(len(result.errors), 0)

    def test_flighthub_requires_connection_identifiers(self):
        site = load_yaml("config/site.example.yaml")
        site["helmValues"]["flighthub"]["enabled"] = True
        secrets = safe_secrets()
        secrets["application"]["flighthubUserToken"] = "flight-token"
        result = validate(
            site,
            load_yaml("inventories/single/hosts.example.yml"),
            secrets,
        )
        self.assertTrue(any("organizationUuid" in error for error in result.errors))
        self.assertTrue(any("projectUuid" in error for error in result.errors))

    def test_release_mode_requires_validated_lock_and_digests(self):
        result = validate_config.ValidationResult()
        validate_config.validate_versions(
            load_yaml("versions.lock.yaml"), release_mode=True, result=result
        )
        self.assertTrue(any("validated" in error for error in result.errors))
        self.assertTrue(any("digest" in error for error in result.errors))
        self.assertTrue(any("compliance.approved" in error for error in result.errors))


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