"""Tests for ``ilum diff`` 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.core.safety import ValuesSnapshot
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_diff(mock_mgr: MagicMock):
    return patch("ilum.cli.diff_cmd.ReleaseManager", return_value=mock_mgr)


class TestDiffCommand:
    def test_diff_help(self, runner: CliRunner) -> None:
        result = runner.invoke(app, ["diff", "--help"])
        assert result.exit_code == 0
        assert "Compare values" in result.output

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

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

    def test_diff_no_differences(self, runner: CliRunner, mock_mgr: MagicMock) -> None:
        mock_mgr.fetch_live_values.return_value = {"a": 1}
        mock_mgr.fetch_computed_values.return_value = {"a": 1}
        with _patch_diff(mock_mgr):
            result = runner.invoke(app, ["diff", "--source", "defaults"])
        assert result.exit_code == 0
        assert "No differences" in result.output

    def test_diff_source_file(self, runner: CliRunner, mock_mgr: MagicMock, tmp_path) -> None:
        vals_file = tmp_path / "custom.yaml"
        vals_file.write_text("ilum-core:\n  enabled: false\n")
        with _patch_diff(mock_mgr):
            result = runner.invoke(
                app, ["diff", "--source", "file", "--values-file", str(vals_file)]
            )
        assert result.exit_code == 0

    def test_diff_source_file_missing_arg(self, runner: CliRunner, mock_mgr: MagicMock) -> None:
        with _patch_diff(mock_mgr):
            result = runner.invoke(app, ["diff", "--source", "file"])
        assert result.exit_code == 1

    def test_diff_source_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_diff(mock_mgr):
            result = runner.invoke(app, ["diff", "--source", "revision", "--revision", "1"])
        assert result.exit_code == 0
        mock_mgr.helm.get_values_at_revision.assert_called_once_with("ilum", 1)

    def test_diff_source_revision_missing_arg(self, runner: CliRunner, mock_mgr: MagicMock) -> None:
        with _patch_diff(mock_mgr):
            result = runner.invoke(app, ["diff", "--source", "revision"])
        assert result.exit_code == 1

    def test_diff_source_snapshot_no_snapshot(self, runner: CliRunner, mock_mgr: MagicMock) -> None:
        with _patch_diff(mock_mgr), patch("ilum.cli.diff_cmd.load_snapshot", return_value=None):
            result = runner.invoke(app, ["diff", "--source", "snapshot"])
        assert result.exit_code == 0
        assert "No CLI snapshot" in result.output

    def test_diff_source_snapshot_with_snapshot(
        self, runner: CliRunner, mock_mgr: MagicMock
    ) -> None:
        snapshot = ValuesSnapshot(
            release="ilum",
            namespace="default",
            timestamp="2024-01-15T12:00:00",
            chart_version="6.7.0",
            values={"ilum-core": {"enabled": True}},
            operation="upgrade",
        )
        with _patch_diff(mock_mgr), patch("ilum.cli.diff_cmd.load_snapshot", return_value=snapshot):
            result = runner.invoke(app, ["diff", "--source", "snapshot"])
        assert result.exit_code == 0

    def test_diff_invalid_source(self, runner: CliRunner, mock_mgr: MagicMock) -> None:
        with _patch_diff(mock_mgr):
            result = runner.invoke(app, ["diff", "--source", "invalid"])
        assert result.exit_code == 1

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