from __future__ import annotations

import base64
import json
from pathlib import Path
from types import SimpleNamespace

import pytest
import respx
import yaml

from omni import AsyncOmniClient, OmniClient
from omni.cli.talos_manager import TalosConfigManager
from omni.state import StateStore


def _config(url: str = "https://omni.example") -> dict:
    return {
        "default_instance": "default",
        "instances": {
            "default": {
                "url": url,
            }
        },
    }


def _talosconfig_payload() -> dict[str, str]:
    content = yaml.safe_dump(
        {
            "context": "upstream",
            "contexts": {
                "upstream": {
                    "endpoints": ["https://omni.example"],
                    "auth": {"siderov1": {"identity": "robot"}},
                }
            },
        },
        sort_keys=False,
    ).encode("utf-8")
    return {"talosconfig": base64.b64encode(content).decode("utf-8")}


@pytest.mark.asyncio()
async def test_async_ensure_talosconfig_writes_managed_context(tmp_path: Path) -> None:
    state_path = tmp_path / "state.db"

    with respx.mock(base_url="https://omni.example") as mock:
        route = mock.post("/api/management.ManagementService/Talosconfig").respond(json=_talosconfig_payload())

        client = AsyncOmniClient(config=_config(), state_path=state_path)
        path = await client.ensure_talosconfig(cluster="cluster-a")
        second = await client.ensure_talosconfig(cluster="cluster-a", ttl_seconds=300)

        assert path == second
        assert route.call_count == 1
        assert path.exists()

        payload = yaml.safe_load(path.read_text(encoding="utf-8"))
        assert payload["context"] == "omni/default/cluster-a"
        assert "omni/default/cluster-a" in payload["contexts"]

        await client.aclose()


@pytest.mark.asyncio()
async def test_resolve_nodes_maps_hostname_to_machine_id(tmp_path: Path) -> None:
    state = StateStore(tmp_path / "state.db")
    node_id = "03ebd680-b6a5-11ed-9210-d2dfd04d4a00"
    hostname = "metal-a8c64g-mfum690s-001"

    class _Resources:
        async def list(self, request: dict[str, str]) -> dict[str, list[str]]:
            assert request["type"] == "MachineStatuses.omni.sidero.dev"
            item = {
                "metadata": {"id": node_id, "labels": {"omni.sidero.dev/cluster": "halceon"}},
                "spec": {"cluster": "halceon", "network": {"hostname": hostname}},
            }
            return {"items": [json.dumps(item)]}

    client = SimpleNamespace(
        state=state,
        instance_name="default",
        instance_config=SimpleNamespace(
            auth=SimpleNamespace(service_account_key=None, service_account_env_aliases=[]),
        ),
        cluster=None,
        resources=_Resources(),
    )
    manager = TalosConfigManager(client)
    assert await manager.resolve_nodes(hostname, cluster="halceon") == node_id
    assert await manager.resolve_nodes(f"{hostname},{node_id}", cluster="halceon") == f"{node_id},{node_id}"
    state.close()


def test_sync_ensure_talosconfig_force_refresh(tmp_path: Path) -> None:
    state_path = tmp_path / "state.db"

    with respx.mock(base_url="https://omni.example") as mock:
        route = mock.post("/api/management.ManagementService/Talosconfig").respond(json=_talosconfig_payload())

        client = OmniClient(config=_config(), state_path=state_path)
        first = client.ensure_talosconfig(cluster="cluster-a")
        second = client.ensure_talosconfig(cluster="cluster-a", force_refresh=True)

        assert first == second
        assert route.call_count == 2
        client.close()


def _service_account_key(name: str, armored: str) -> str:
    payload = {"name": name, "pgp_key": armored}
    return base64.b64encode(json.dumps(payload).encode("utf-8")).decode("utf-8")


def test_ensure_siderov1_keys_dir_writes_key(tmp_path: Path) -> None:
    state = StateStore(tmp_path / "state.db")
    context = "omni/default/cluster-a"
    identity = "robot@serviceaccount.omni.sidero.dev"
    talosconfig_path = tmp_path / "managed-talosconfig.yaml"
    talosconfig_path.write_text(
        yaml.safe_dump(
            {
                "context": context,
                "contexts": {
                    context: {
                        "endpoints": ["https://omni.example"],
                        "auth": {"siderov1": {"identity": identity}},
                    }
                },
            },
            sort_keys=False,
        ),
        encoding="utf-8",
    )

    armored = "-----BEGIN PGP PRIVATE KEY BLOCK-----\nabc\n-----END PGP PRIVATE KEY BLOCK-----\n"
    client = SimpleNamespace(
        state=state,
        instance_name="default",
        instance_config=SimpleNamespace(
            auth=SimpleNamespace(
                service_account_key=_service_account_key(identity, armored),
                service_account_env_aliases=[],
            )
        ),
        cluster=None,
    )
    manager = TalosConfigManager(client)
    keys_dir = manager.ensure_siderov1_keys_dir(talosconfig_path=talosconfig_path, context=context)

    assert keys_dir is not None
    key_path = keys_dir / f"{context}-{identity}.pgp"
    assert key_path.exists()
    assert key_path.read_text(encoding="utf-8") == armored
    state.close()


def test_ensure_siderov1_keys_dir_returns_none_without_service_account(tmp_path: Path) -> None:
    state = StateStore(tmp_path / "state.db")
    context = "omni/default/cluster-a"
    identity = "robot@serviceaccount.omni.sidero.dev"
    talosconfig_path = tmp_path / "managed-talosconfig.yaml"
    talosconfig_path.write_text(
        yaml.safe_dump(
            {
                "context": context,
                "contexts": {
                    context: {
                        "endpoints": ["https://omni.example"],
                        "auth": {"siderov1": {"identity": identity}},
                    }
                },
            },
            sort_keys=False,
        ),
        encoding="utf-8",
    )

    client = SimpleNamespace(
        state=state,
        instance_name="default",
        instance_config=SimpleNamespace(
            auth=SimpleNamespace(
                service_account_key=None,
                service_account_env_aliases=[],
            )
        ),
        cluster=None,
    )
    manager = TalosConfigManager(client)
    assert manager.ensure_siderov1_keys_dir(talosconfig_path=talosconfig_path, context=context) is None
    state.close()
