"""Tests for prerequisite checks with mocked subprocess calls."""

from __future__ import annotations

import subprocess
from unittest.mock import MagicMock, patch

from exokit.core.prereqs import (
    check_all,
    check_apx,
    check_databricks_auth,
    check_databricks_cli,
    check_node,
    check_uv,
)


class TestCheckDatabricksCli:
    """Tests for check_databricks_cli."""

    @patch("exokit.core.prereqs.subprocess.run")
    def test_found(self, mock_run: MagicMock) -> None:
        mock_run.return_value = subprocess.CompletedProcess(
            args=["databricks", "--version"],
            returncode=0,
            stdout="Databricks CLI v0.230.0",
            stderr="",
        )
        result = check_databricks_cli()
        assert result.passed is True
        assert result.name == "databricks-cli"
        assert result.version == "0.230.0"
        assert "Databricks CLI" in result.message

    @patch("exokit.core.prereqs.subprocess.run")
    def test_not_found(self, mock_run: MagicMock) -> None:
        mock_run.side_effect = FileNotFoundError()
        result = check_databricks_cli()
        assert result.passed is False
        assert "not found" in result.message.lower()

    @patch("exokit.core.prereqs.subprocess.run")
    def test_timeout(self, mock_run: MagicMock) -> None:
        mock_run.side_effect = subprocess.TimeoutExpired(cmd="databricks", timeout=10)
        result = check_databricks_cli()
        assert result.passed is False
        assert "timed out" in result.message.lower()


class TestCheckUv:
    """Tests for check_uv."""

    @patch("exokit.core.prereqs.subprocess.run")
    def test_found(self, mock_run: MagicMock) -> None:
        mock_run.return_value = subprocess.CompletedProcess(
            args=["uv", "--version"],
            returncode=0,
            stdout="uv 0.5.14",
            stderr="",
        )
        result = check_uv()
        assert result.passed is True
        assert result.name == "uv"
        assert result.version == "0.5.14"

    @patch("exokit.core.prereqs.subprocess.run")
    def test_not_found(self, mock_run: MagicMock) -> None:
        mock_run.side_effect = FileNotFoundError()
        result = check_uv()
        assert result.passed is False


class TestCheckNode:
    """Tests for check_node."""

    @patch("exokit.core.prereqs.subprocess.run")
    def test_found(self, mock_run: MagicMock) -> None:
        mock_run.return_value = subprocess.CompletedProcess(
            args=["node", "--version"],
            returncode=0,
            stdout="v20.11.0",
            stderr="",
        )
        result = check_node()
        assert result.passed is True
        assert result.name == "node"
        assert result.version == "20.11.0"

    @patch("exokit.core.prereqs.subprocess.run")
    def test_not_found(self, mock_run: MagicMock) -> None:
        mock_run.side_effect = FileNotFoundError()
        result = check_node()
        assert result.passed is False


class TestCheckApx:
    """Tests for check_apx."""

    @patch("exokit.core.prereqs.subprocess.run")
    def test_found(self, mock_run: MagicMock) -> None:
        mock_run.return_value = subprocess.CompletedProcess(
            args=["apx", "--version"],
            returncode=0,
            stdout="apx version 1.2.0",
            stderr="",
        )
        result = check_apx()
        assert result.passed is True
        assert result.name == "apx"
        assert result.version == "1.2.0"

    @patch("exokit.core.prereqs.subprocess.run")
    def test_not_found(self, mock_run: MagicMock) -> None:
        mock_run.side_effect = FileNotFoundError()
        result = check_apx()
        assert result.passed is False

    @patch("exokit.core.prereqs.subprocess.run")
    def test_non_zero_exit(self, mock_run: MagicMock) -> None:
        mock_run.return_value = subprocess.CompletedProcess(
            args=["apx", "--version"],
            returncode=1,
            stdout="",
            stderr="unknown command",
        )
        result = check_apx()
        assert result.passed is False


class TestCheckDatabricksAuth:
    """Tests for check_databricks_auth."""

    @patch("exokit.core.prereqs.subprocess.run")
    def test_auth_configured(self, mock_run: MagicMock) -> None:
        mock_run.return_value = subprocess.CompletedProcess(
            args=["databricks", "auth", "describe", "--profile", "DEFAULT"],
            returncode=0,
            stdout="Host: https://adb-123.azuredatabricks.net\nUser: user@test.com",
            stderr="",
        )
        result = check_databricks_auth(profile="DEFAULT")
        assert result.passed is True
        assert result.name == "databricks-auth-DEFAULT"

    @patch("exokit.core.prereqs.subprocess.run")
    def test_auth_not_configured(self, mock_run: MagicMock) -> None:
        mock_run.return_value = subprocess.CompletedProcess(
            args=["databricks", "auth", "describe", "--profile", "PROD"],
            returncode=1,
            stdout="",
            stderr="Error: no configuration found",
        )
        result = check_databricks_auth(profile="PROD")
        assert result.passed is False
        assert "PROD" in result.name

    @patch("exokit.core.prereqs.subprocess.run")
    def test_custom_profile(self, mock_run: MagicMock) -> None:
        mock_run.return_value = subprocess.CompletedProcess(
            args=["databricks", "auth", "describe", "--profile", "staging"],
            returncode=0,
            stdout="Host: https://adb-staging.azuredatabricks.net",
            stderr="",
        )
        result = check_databricks_auth(profile="staging")
        assert result.passed is True
        assert result.name == "databricks-auth-staging"


class TestCheckAll:
    """Tests for check_all function."""

    @patch("exokit.core.prereqs.subprocess.run")
    def test_returns_all_results(self, mock_run: MagicMock) -> None:
        mock_run.return_value = subprocess.CompletedProcess(
            args=[],
            returncode=0,
            stdout="version 1.0.0",
            stderr="",
        )
        results = check_all()
        assert len(results) == 7
        names = {r.name for r in results}
        assert "databricks-cli" in names
        assert "databricks-profiles" in names
        assert "uv" in names
        assert "node" in names
        assert "apx" in names
        assert "ai-dev-kit" in names
        assert "databricks-auth-DEFAULT" in names

    @patch("exokit.core.prereqs.subprocess.run")
    def test_custom_profile_passed(self, mock_run: MagicMock) -> None:
        mock_run.return_value = subprocess.CompletedProcess(
            args=[],
            returncode=0,
            stdout="version 1.0.0",
            stderr="",
        )
        results = check_all(databricks_profile="STAGING")
        auth_result = next(r for r in results if r.name.startswith("databricks-auth"))
        assert auth_result.name == "databricks-auth-STAGING"

    @patch("exokit.core.prereqs.subprocess.run")
    def test_mixed_results(self, mock_run: MagicMock) -> None:
        """Some tools installed, some not."""

        def side_effect(command: list[str], **kwargs: object) -> subprocess.CompletedProcess[str]:
            cmd_name = command[0]
            if cmd_name in ("uv", "node"):
                return subprocess.CompletedProcess(
                    args=command, returncode=0, stdout="v1.0.0", stderr=""
                )
            raise FileNotFoundError()

        mock_run.side_effect = side_effect
        results = check_all()
        passed = [r for r in results if r.passed]
        failed = [r for r in results if not r.passed]
        assert len(passed) >= 2
        assert len(failed) >= 2
