"""Tests for ``ilum values`` 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.core.helm import HelmResult
from ilum.core.release import ReleaseInfo
from ilum.errors import ReleaseNotFoundError


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


@pytest.fixture()
def mock_mgr() -> MagicMock:
    from ilum.core.modules import ModuleResolver
    from ilum.core.release import ReleaseManager

    mgr = MagicMock(spec=ReleaseManager)
    mgr.resolver = ModuleResolver()
    mgr.helm = MagicMock()
    mgr.helm.namespace = "default"
    mgr.get_release_info.return_value = ReleaseInfo(
        name="ilum",
        namespace="default",
        status="deployed",
        chart="ilum",
        chart_version="6.7.0",
        revision=3,
        last_deployed="2024-01-15 12:00:00",
    )
    mgr.fetch_live_values.return_value = {
        "ilum-core": {"enabled": True, "replicaCount": 1},
        "ilum-ui": {"enabled": True},
    }
    mgr.fetch_computed_values.return_value = {
        "ilum-core": {"enabled": True, "replicaCount": 1, "image": "ilum/core:latest"},
        "ilum-ui": {"enabled": True, "image": "ilum/ui:latest"},
    }
    return mgr


def _patch_values(mock_mgr: MagicMock):
    return patch("ilum.cli.values_cmd.ReleaseManager", return_value=mock_mgr)


class TestValuesCommand:
    def test_values_help(self, runner: CliRunner) -> None:
        result = runner.invoke(app, ["values", "--help"])
        assert result.exit_code == 0
        assert "View or export" in result.output

    def test_values_default_table(self, runner: CliRunner, mock_mgr: MagicMock) -> None:
        with _patch_values(mock_mgr):
            result = runner.invoke(app, ["values"])
        assert result.exit_code == 0
        assert "ilum-core" in result.output

    def test_values_json_output(self, runner: CliRunner, mock_mgr: MagicMock) -> None:
        with _patch_values(mock_mgr):
            result = runner.invoke(app, ["--output", "json", "values"])
        assert result.exit_code == 0
        assert "ilum-core" in result.output

    def test_values_yaml_output(self, runner: CliRunner, mock_mgr: MagicMock) -> None:
        with _patch_values(mock_mgr):
            result = runner.invoke(app, ["--output", "yaml", "values"])
        assert result.exit_code == 0
        assert "ilum-core" in result.output

    def test_values_csv_output(self, runner: CliRunner, mock_mgr: MagicMock) -> None:
        with _patch_values(mock_mgr):
            result = runner.invoke(app, ["--output", "csv", "values"])
        assert result.exit_code == 0

    def test_values_path_filter(self, runner: CliRunner, mock_mgr: MagicMock) -> None:
        with _patch_values(mock_mgr):
            result = runner.invoke(app, ["values", "--path", "ilum-core.replicaCount"])
        assert result.exit_code == 0
        assert "1" in result.output

    def test_values_path_nested(self, runner: CliRunner, mock_mgr: MagicMock) -> None:
        with _patch_values(mock_mgr):
            result = runner.invoke(app, ["values", "--path", "ilum-core"])
        assert result.exit_code == 0
        assert "enabled" in result.output

    def test_values_path_not_found(self, runner: CliRunner, mock_mgr: MagicMock) -> None:
        with _patch_values(mock_mgr):
            result = runner.invoke(app, ["values", "--path", "nonexistent.path"])
        assert result.exit_code != 0

    def test_values_all(self, runner: CliRunner, mock_mgr: MagicMock) -> None:
        with _patch_values(mock_mgr):
            result = runner.invoke(app, ["values", "--all"])
        assert result.exit_code == 0
        mock_mgr.fetch_computed_values.assert_called_once()

    def test_values_diff(self, runner: CliRunner, mock_mgr: MagicMock) -> None:
        with _patch_values(mock_mgr):
            result = runner.invoke(app, ["values", "--diff"])
        assert result.exit_code == 0

    def test_values_diff_json(self, runner: CliRunner, mock_mgr: MagicMock) -> None:
        with _patch_values(mock_mgr):
            result = runner.invoke(app, ["--output", "json", "values", "--diff"])
        assert result.exit_code == 0
        assert "added" in result.output

    def test_values_revision(self, runner: CliRunner, mock_mgr: MagicMock) -> None:
        mock_mgr.helm.get_values_at_revision.return_value = HelmResult(
            returncode=0,
            stdout="",
            stderr="",
            json_data={"ilum-core": {"enabled": True}},
        )
        with _patch_values(mock_mgr):
            result = runner.invoke(app, ["values", "--revision", "2"])
        assert result.exit_code == 0
        mock_mgr.helm.get_values_at_revision.assert_called_once_with("ilum", 2)

    def test_values_export(self, runner: CliRunner, mock_mgr: MagicMock, tmp_path) -> None:
        export_path = tmp_path / "exported.yaml"
        with _patch_values(mock_mgr):
            result = runner.invoke(app, ["values", "--export", str(export_path)])
        assert result.exit_code == 0
        assert export_path.exists()
        content = export_path.read_text()
        assert "ilum-core" in content

    def test_values_release_not_found(self, runner: CliRunner, mock_mgr: MagicMock) -> None:
        mock_mgr.get_release_info.side_effect = ReleaseNotFoundError("not found")
        with _patch_values(mock_mgr):
            result = runner.invoke(app, ["values"])
        assert result.exit_code == 1

    def test_values_diff_empty(self, runner: CliRunner, mock_mgr: MagicMock) -> None:
        """When user values and computed values are equal, show info message."""
        mock_mgr.fetch_live_values.return_value = {"a": 1}
        mock_mgr.fetch_computed_values.return_value = {"a": 1}
        with _patch_values(mock_mgr):
            result = runner.invoke(app, ["values", "--diff"])
        assert result.exit_code == 0
        assert "No differences" in result.output
