"""Tests for ``ilum top`` command."""

from __future__ import annotations

from unittest.mock import MagicMock, patch

import pytest
from typer.testing import CliRunner

from ilum.cli.main import app
from ilum.cli.top_cmd import (
    _build_label_map,
    _collect_usage,
    _format_cpu,
    _format_mem,
    _map_pod_to_module,
    _pct,
)
from ilum.core.kubernetes import PodMetrics, PodResources


@pytest.fixture()
def runner() -> CliRunner:
    return CliRunner()


@pytest.fixture()
def mock_k8s() -> MagicMock:
    from ilum.core.kubernetes import KubeClient

    mock = MagicMock(spec=KubeClient)
    mock.metrics_available.return_value = True
    mock.list_pod_metrics.return_value = [
        PodMetrics(
            name="ilum-core-0",
            namespace="default",
            cpu_millicores=250,
            memory_bytes=512 * 1024 * 1024,
        ),
        PodMetrics(
            name="ilum-ui-0",
            namespace="default",
            cpu_millicores=100,
            memory_bytes=128 * 1024 * 1024,
        ),
    ]
    mock.list_pod_resources.return_value = [
        PodResources(
            name="ilum-core-0",
            cpu_request_millicores=500,
            cpu_limit_millicores=1000,
            memory_request_bytes=1024 * 1024 * 1024,
            memory_limit_bytes=2 * 1024 * 1024 * 1024,
        ),
        PodResources(
            name="ilum-ui-0",
            cpu_request_millicores=200,
            cpu_limit_millicores=500,
            memory_request_bytes=256 * 1024 * 1024,
            memory_limit_bytes=512 * 1024 * 1024,
        ),
    ]
    return mock


def _patch_top(mock_k8s: MagicMock):
    return patch("ilum.cli.top_cmd.KubeClient", return_value=mock_k8s)


class TestFormatHelpers:
    def test_format_cpu_millicores(self) -> None:
        assert _format_cpu(250) == "250m"

    def test_format_cpu_cores(self) -> None:
        assert _format_cpu(1500) == "1.5"

    def test_format_mem_bytes(self) -> None:
        assert _format_mem(512) == "512B"

    def test_format_mem_ki(self) -> None:
        assert _format_mem(2048) == "2Ki"

    def test_format_mem_mi(self) -> None:
        assert _format_mem(256 * 1024 * 1024) == "256Mi"

    def test_format_mem_gi(self) -> None:
        assert _format_mem(2 * 1024 * 1024 * 1024) == "2.0Gi"

    def test_pct(self) -> None:
        assert _pct(250, 500) == "50%"

    def test_pct_zero_request(self) -> None:
        assert _pct(250, 0) == "\u2014"


class TestLabelMap:
    def test_build_label_map(self) -> None:
        label_map = _build_label_map()
        assert "ilum-core" in label_map
        assert label_map["ilum-core"] == "core"

    def test_map_pod_to_module(self) -> None:
        label_map = _build_label_map()
        assert _map_pod_to_module("ilum-core-0", label_map) == "core"

    def test_map_unknown_pod(self) -> None:
        label_map = _build_label_map()
        assert _map_pod_to_module("random-pod-123", label_map) == ""


class TestCollectUsage:
    def test_aggregation(self, mock_k8s: MagicMock) -> None:
        usages = _collect_usage(mock_k8s, "default")
        assert len(usages) >= 1
        names = {u.name for u in usages}
        assert "core" in names

    def test_module_filter(self, mock_k8s: MagicMock) -> None:
        usages = _collect_usage(mock_k8s, "default", "core")
        names = {u.name for u in usages}
        assert names == {"core"}


class TestTopCommand:
    def test_top_help(self, runner: CliRunner) -> None:
        result = runner.invoke(app, ["top", "--help"])
        assert result.exit_code == 0
        assert "resource usage" in result.output.lower() or "Resource" in result.output

    def test_top_table_output(self, runner: CliRunner, mock_k8s: MagicMock) -> None:
        with _patch_top(mock_k8s):
            result = runner.invoke(app, ["top"])
        assert result.exit_code == 0
        assert "TOTAL" in result.output

    def test_top_json_output(self, runner: CliRunner, mock_k8s: MagicMock) -> None:
        with _patch_top(mock_k8s):
            result = runner.invoke(app, ["--output", "json", "top"])
        assert result.exit_code == 0
        assert "module" in result.output

    def test_top_csv_output(self, runner: CliRunner, mock_k8s: MagicMock) -> None:
        with _patch_top(mock_k8s):
            result = runner.invoke(app, ["--output", "csv", "top"])
        assert result.exit_code == 0
        assert "module" in result.output

    def test_top_sort_by_cpu(self, runner: CliRunner, mock_k8s: MagicMock) -> None:
        with _patch_top(mock_k8s):
            result = runner.invoke(app, ["top", "--sort-by", "cpu"])
        assert result.exit_code == 0

    def test_top_sort_by_memory(self, runner: CliRunner, mock_k8s: MagicMock) -> None:
        with _patch_top(mock_k8s):
            result = runner.invoke(app, ["top", "--sort-by", "memory"])
        assert result.exit_code == 0

    def test_top_module_filter(self, runner: CliRunner, mock_k8s: MagicMock) -> None:
        with _patch_top(mock_k8s):
            result = runner.invoke(app, ["top", "core"])
        assert result.exit_code == 0

    def test_top_no_metrics(self, runner: CliRunner, mock_k8s: MagicMock) -> None:
        mock_k8s.metrics_available.return_value = False
        with _patch_top(mock_k8s):
            result = runner.invoke(app, ["top"])
        assert result.exit_code == 1
        assert "Metrics" in result.output or "metrics" in result.output
