"""Tests for the Helm subprocess wrapper."""

from __future__ import annotations

import subprocess
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest

from ilum.core.helm import HelmClient
from ilum.errors import HelmError, HelmTimeoutError, PrerequisiteError


@pytest.fixture()
def helm() -> HelmClient:
    return HelmClient(kubecontext="test-ctx", namespace="test-ns")


@pytest.fixture()
def dry_helm() -> HelmClient:
    return HelmClient(dry_run=True)


class TestHelmClient:
    @patch("subprocess.run")
    def test_run_adds_context_and_namespace(self, mock_run: MagicMock, helm: HelmClient) -> None:
        mock_run.return_value = subprocess.CompletedProcess(
            args=[], returncode=0, stdout="ok", stderr=""
        )
        helm._run(["version", "--short"])
        cmd = mock_run.call_args[0][0]
        assert "--kube-context" in cmd
        assert "test-ctx" in cmd
        assert "--namespace" in cmd
        assert "test-ns" in cmd

    @patch("subprocess.run")
    def test_run_raises_helm_error_on_failure(self, mock_run: MagicMock, helm: HelmClient) -> None:
        mock_run.return_value = subprocess.CompletedProcess(
            args=[], returncode=1, stdout="", stderr="Error: something broke"
        )
        with pytest.raises(HelmError, match="something broke"):
            helm._run(["install", "bad"])

    @patch("subprocess.run")
    def test_run_raises_prerequisite_error_when_helm_missing(
        self, mock_run: MagicMock, helm: HelmClient
    ) -> None:
        mock_run.side_effect = FileNotFoundError("No such file or directory: 'helm'")
        with pytest.raises(PrerequisiteError, match="helm binary not found"):
            helm._run(["version"])

    @patch("subprocess.run")
    def test_run_raises_timeout_error(self, mock_run: MagicMock, helm: HelmClient) -> None:
        mock_run.side_effect = subprocess.TimeoutExpired(cmd="helm", timeout=60)
        with pytest.raises(HelmTimeoutError):
            helm._run(["install", "slow"], timeout_seconds=60)

    @patch("subprocess.run")
    def test_run_captures_json(self, mock_run: MagicMock, helm: HelmClient) -> None:
        mock_run.return_value = subprocess.CompletedProcess(
            args=[], returncode=0, stdout='[{"name":"ilum"}]', stderr=""
        )
        result = helm._run(["list", "--output", "json"], capture_json=True)
        assert result.json_data == [{"name": "ilum"}]

    def test_dry_run_skips_execution(self, dry_helm: HelmClient) -> None:
        result = dry_helm._run(["install", "ilum", "ilum/ilum"])
        assert result.returncode == 0
        assert "dry-run" in result.stdout.lower() or "would execute" in result.stdout.lower()

    @patch("subprocess.run")
    def test_install_default_no_atomic_no_wait(self, mock_run: MagicMock, helm: HelmClient) -> None:
        mock_run.return_value = subprocess.CompletedProcess(
            args=[], returncode=0, stdout="ok", stderr=""
        )
        helm.install("ilum", "ilum/ilum")
        cmd = mock_run.call_args[0][0]
        assert "--atomic" not in cmd
        assert "--wait" not in cmd
        assert "--timeout" in cmd
        assert "install" in cmd

    @patch("subprocess.run")
    def test_install_adds_values_and_set_flags(self, mock_run: MagicMock, helm: HelmClient) -> None:
        mock_run.return_value = subprocess.CompletedProcess(
            args=[], returncode=0, stdout="ok", stderr=""
        )
        helm.install(
            "ilum",
            "ilum/ilum",
            values_files=[Path("/tmp/vals.yaml")],
            set_flags=["kafka.enabled=true"],
        )
        cmd = mock_run.call_args[0][0]
        assert "-f" in cmd
        assert "/tmp/vals.yaml" in cmd
        assert "--set" in cmd
        assert "kafka.enabled=true" in cmd

    @patch("subprocess.run")
    def test_upgrade_command(self, mock_run: MagicMock, helm: HelmClient) -> None:
        mock_run.return_value = subprocess.CompletedProcess(
            args=[], returncode=0, stdout="ok", stderr=""
        )
        helm.upgrade("ilum", "ilum/ilum")
        cmd = mock_run.call_args[0][0]
        assert "upgrade" in cmd
        assert "--atomic" not in cmd

    @patch("subprocess.run")
    def test_list_releases_json(self, mock_run: MagicMock, helm: HelmClient) -> None:
        mock_run.return_value = subprocess.CompletedProcess(
            args=[], returncode=0, stdout="[]", stderr=""
        )
        result = helm.list_releases()
        assert result.json_data is not None
        cmd = mock_run.call_args[0][0]
        assert "--all" in cmd

    @patch("subprocess.run")
    def test_version_command(self, mock_run: MagicMock, helm: HelmClient) -> None:
        mock_run.return_value = subprocess.CompletedProcess(
            args=[], returncode=0, stdout="v3.14.0", stderr=""
        )
        result = helm.version()
        assert "v3.14.0" in result.stdout

    @patch("subprocess.run")
    def test_uninstall_command(self, mock_run: MagicMock, helm: HelmClient) -> None:
        mock_run.return_value = subprocess.CompletedProcess(
            args=[], returncode=0, stdout="ok", stderr=""
        )
        helm.uninstall("ilum")
        cmd = mock_run.call_args[0][0]
        assert "uninstall" in cmd
        assert "ilum" in cmd

    @patch("subprocess.run")
    def test_result_records_duration(self, mock_run: MagicMock, helm: HelmClient) -> None:
        mock_run.return_value = subprocess.CompletedProcess(
            args=[], returncode=0, stdout="ok", stderr=""
        )
        result = helm._run(["version"])
        assert result.duration_seconds >= 0.0


class TestHelmPreview:
    def test_preview_install_returns_full_command(self, helm: HelmClient) -> None:
        cmd = helm.preview_install("ilum", "ilum/ilum", version="6.7.0")
        assert cmd[0] == "helm"
        assert "install" in cmd
        assert "ilum" in cmd
        assert "ilum/ilum" in cmd
        assert "--version" in cmd
        assert "6.7.0" in cmd
        assert "--atomic" not in cmd
        assert "--kube-context" in cmd
        assert "test-ctx" in cmd
        assert "--namespace" in cmd
        assert "test-ns" in cmd

    def test_preview_upgrade_returns_full_command(self, helm: HelmClient) -> None:
        cmd = helm.preview_upgrade("ilum", "ilum/ilum", set_flags=["foo=bar"], version="6.8.0")
        assert cmd[0] == "helm"
        assert "upgrade" in cmd
        assert "--set" in cmd
        assert "foo=bar" in cmd
        assert "--version" in cmd
        assert "6.8.0" in cmd
        assert "--kube-context" in cmd

    def test_preview_uninstall_returns_full_command(self, helm: HelmClient) -> None:
        cmd = helm.preview_uninstall("ilum")
        assert cmd[0] == "helm"
        assert "uninstall" in cmd
        assert "ilum" in cmd
        assert "--kube-context" in cmd
        assert "test-ctx" in cmd

    def test_preview_install_includes_values_files(self, helm: HelmClient) -> None:
        cmd = helm.preview_install("ilum", "ilum/ilum", values_files=[Path("/tmp/vals.yaml")])
        assert "-f" in cmd
        assert "/tmp/vals.yaml" in cmd

    def test_build_full_cmd_no_context(self) -> None:
        h = HelmClient(namespace="default")
        cmd = h._build_full_cmd(["version"])
        assert "helm" in cmd
        assert "--kube-context" not in cmd
        assert "--namespace" in cmd
