"""Tests for complyform doctor command."""

from __future__ import annotations

from typing import TYPE_CHECKING
from unittest.mock import MagicMock, patch

if TYPE_CHECKING:
    from pathlib import Path

from complyform.cli.doctor_cmd import (
    DoctorCheckResult,
    DoctorResults,
    _check_anthropic_key,
    _check_cache_status,
    _check_checkov,
    _check_cloud_credentials,
    _check_cloud_intelligence_apis,
    _check_complyform_version,
    _check_config_permissions,
    _check_disk_space,
    _check_github_token,
    _check_iac_binary,
    _check_infracost,
    _check_latest_version,
    _check_license_status,
    _check_network,
    _check_profile_bundle,
    _check_python_version,
    _check_shell_completion,
    build_diagnostic_string,
    run_doctor_checks,
)


class TestCheckPythonVersion:
    def test_pass(self) -> None:
        result = _check_python_version(verbose=False)
        assert result.number == 1
        assert result.status == "pass"
        assert "Python" in result.label
        assert result.timing_ms is None

    def test_verbose_has_timing(self) -> None:
        result = _check_python_version(verbose=True)
        assert result.timing_ms is not None


class TestCheckComplyformVersion:
    def test_always_info(self) -> None:
        result = _check_complyform_version(verbose=False)
        assert result.status == "info"
        assert "complyform" in result.label


class TestCheckIacBinary:
    @patch("complyform.core.iac_binary.resolve_iac_binary")
    def test_found(self, mock_resolve: MagicMock) -> None:
        from complyform.core.iac_binary import IaCBinary

        mock_resolve.return_value = IaCBinary(
            path="/usr/bin/tofu", name="tofu", version="1.9.0"
        )
        result = _check_iac_binary(verbose=False)
        assert result.status == "pass"
        assert "tofu" in result.label

    @patch(
        "complyform.core.iac_binary.resolve_iac_binary",
        side_effect=Exception("not found"),
    )
    def test_not_found(self, mock_resolve: MagicMock) -> None:
        result = _check_iac_binary(verbose=False)
        assert result.status == "warn"
        assert result.fix is not None


class TestCheckCheckov:
    @patch("complyform.cli.doctor_cmd.shutil.which", return_value="/usr/bin/checkov")
    @patch(
        "complyform.cli.doctor_cmd._run_subprocess",
        return_value=("3.2.1", "", 0),
    )
    def test_found(self, mock_sub: MagicMock, mock_which: MagicMock) -> None:
        result = _check_checkov(verbose=False)
        assert result.status == "pass"
        assert "3.2.1" in result.label

    @patch("complyform.cli.doctor_cmd.shutil.which", return_value=None)
    def test_not_found(self, mock_which: MagicMock) -> None:
        result = _check_checkov(verbose=False)
        assert result.status == "error"
        assert result.fix is not None


class TestCheckCloudCredentials:
    @patch(
        "complyform.cli.doctor_cmd._run_subprocess",
        return_value=("token123", "", 0),
    )
    def test_gcp_valid(self, mock_sub: MagicMock) -> None:
        result = _check_cloud_credentials(verbose=False, cloud="gcp")
        assert result.status == "pass"

    @patch(
        "complyform.cli.doctor_cmd._run_subprocess",
        return_value=("", "error", 1),
    )
    def test_gcp_invalid(self, mock_sub: MagicMock) -> None:
        result = _check_cloud_credentials(verbose=False, cloud="gcp")
        assert result.status == "warn"

    def test_no_cloud(self) -> None:
        result = _check_cloud_credentials(verbose=False, cloud=None)
        assert result.status == "info"


class TestCheckLicenseStatus:
    @patch("complyform.core.auth.license.LicenseManager")
    def test_no_license(self, mock_mgr_cls: MagicMock) -> None:
        mock_mgr_cls.return_value.load_cache.return_value = None
        result = _check_license_status(verbose=False)
        assert result.status == "info"
        assert "Community" in result.message

    @patch("complyform.core.auth.license.LicenseManager")
    def test_with_license(self, mock_mgr_cls: MagicMock) -> None:
        cache = MagicMock()
        cache.tier = "team"
        cache.expires_at = "2027-02-25"
        mock_mgr_cls.return_value.load_cache.return_value = cache
        result = _check_license_status(verbose=False)
        assert "Team" in result.label


class TestCheckProfileBundle:
    @patch("complyform.cli.doctor_cmd.Path.home")
    def test_no_profiles(self, mock_home: MagicMock, tmp_path: Path) -> None:
        mock_home.return_value = tmp_path
        result = _check_profile_bundle(verbose=False)
        assert result.status == "info"
        assert "No profiles" in result.label

    @patch("complyform.cli.doctor_cmd.Path.home")
    def test_with_profiles(self, mock_home: MagicMock, tmp_path: Path) -> None:
        mock_home.return_value = tmp_path
        profiles_dir = tmp_path / ".complyform" / "profiles"
        profiles_dir.mkdir(parents=True)
        (profiles_dir / "VERSION").write_text("2026.03.08")
        (profiles_dir / "soc2").mkdir()
        (profiles_dir / "hipaa").mkdir()

        result = _check_profile_bundle(verbose=False)
        assert "2026.03.08" in result.label
        assert "2 frameworks" in result.label


class TestCheckCacheStatus:
    @patch("complyform.cli.doctor_cmd.Path.home")
    def test_no_cache(self, mock_home: MagicMock, tmp_path: Path) -> None:
        mock_home.return_value = tmp_path
        result = _check_cache_status(verbose=False)
        assert "empty" in result.label

    @patch("complyform.cli.doctor_cmd.Path.home")
    def test_with_cache(self, mock_home: MagicMock, tmp_path: Path) -> None:
        mock_home.return_value = tmp_path
        cache_dir = tmp_path / ".complyform" / "cache"
        scan_dir = cache_dir / "scan1"
        scan_dir.mkdir(parents=True)
        (scan_dir / "data.json").write_text("{}")

        result = _check_cache_status(verbose=False)
        assert "1 scans" in result.label


class TestCheckInfracost:
    @patch("complyform.cli.doctor_cmd.shutil.which", return_value=None)
    def test_not_found(self, mock_which: MagicMock) -> None:
        result = _check_infracost(verbose=False)
        assert result.status == "info"
        assert "not found" in result.label


class TestCheckCloudIntelligenceApis:
    def test_community_tier_skips(self) -> None:
        results = _check_cloud_intelligence_apis(
            verbose=False, tier="community", cloud="gcp"
        )
        assert len(results) == 0

    def test_pro_tier_skips(self) -> None:
        results = _check_cloud_intelligence_apis(verbose=False, tier="pro", cloud="gcp")
        assert len(results) == 0

    @patch(
        "complyform.cli.doctor_cmd._run_subprocess",
        return_value=("recommender.googleapis.com", "", 0),
    )
    def test_team_tier_gcp(self, mock_sub: MagicMock) -> None:
        results = _check_cloud_intelligence_apis(
            verbose=False, tier="team", cloud="gcp"
        )
        assert len(results) >= 1


class TestCheckDiskSpace:
    @patch("complyform.cli.doctor_cmd.shutil.disk_usage")
    @patch("complyform.cli.doctor_cmd.Path.home")
    def test_enough_space(
        self, mock_home: MagicMock, mock_usage: MagicMock, tmp_path: Path
    ) -> None:
        mock_home.return_value = tmp_path
        mock_usage.return_value = MagicMock(free=500 * 1024 * 1024)
        result = _check_disk_space(verbose=False)
        assert result.status == "pass"

    @patch("complyform.cli.doctor_cmd.shutil.disk_usage")
    @patch("complyform.cli.doctor_cmd.Path.home")
    def test_low_space(
        self, mock_home: MagicMock, mock_usage: MagicMock, tmp_path: Path
    ) -> None:
        mock_home.return_value = tmp_path
        mock_usage.return_value = MagicMock(free=50 * 1024 * 1024)
        result = _check_disk_space(verbose=False)
        assert result.status == "warn"


class TestCheckLatestVersion:
    def test_community_skips(self) -> None:
        result = _check_latest_version(verbose=False, tier="community")
        assert result.status == "info"
        assert "skipped" in result.message

    @patch("httpx.get", side_effect=Exception("no network"))
    def test_network_failure(self, mock_get: MagicMock) -> None:
        result = _check_latest_version(verbose=False, tier="pro")
        assert result.status == "info"
        assert "network" in result.message.lower()


class TestCheckNetwork:
    def test_community_skips(self) -> None:
        result = _check_network(verbose=False, tier="community")
        assert result.status == "info"
        assert "skipped" in result.message

    @patch("httpx.head", side_effect=Exception("timeout"))
    def test_unreachable(self, mock_head: MagicMock) -> None:
        result = _check_network(verbose=False, tier="team")
        assert result.status == "warn"


class TestCheckConfigPermissions:
    @patch("complyform.cli.doctor_cmd.Path.home")
    def test_correct_permissions(self, mock_home: MagicMock, tmp_path: Path) -> None:
        mock_home.return_value = tmp_path
        config_file = tmp_path / ".complyform" / "config.yaml"
        config_file.parent.mkdir(parents=True)
        config_file.write_text("tier: community")
        config_file.chmod(0o600)
        result = _check_config_permissions(verbose=False)
        assert result.status == "pass"

    @patch("complyform.cli.doctor_cmd.Path.home")
    def test_wrong_permissions(self, mock_home: MagicMock, tmp_path: Path) -> None:
        mock_home.return_value = tmp_path
        config_file = tmp_path / ".complyform" / "config.yaml"
        config_file.parent.mkdir(parents=True)
        config_file.write_text("tier: community")
        config_file.chmod(0o644)
        result = _check_config_permissions(verbose=False)
        assert result.status == "warn"


class TestCheckShellCompletion:
    def test_not_installed(self) -> None:
        result = _check_shell_completion(verbose=False)
        # May be pass or info depending on actual system
        assert result.status in ("pass", "info")


class TestCheckGithubToken:
    def test_no_token(self) -> None:
        result = _check_github_token(verbose=False, config_token=None)
        assert result.status == "info"

    def test_valid_ghp(self) -> None:
        result = _check_github_token(verbose=False, config_token="ghp_abc123xyz")
        assert result.status == "pass"

    def test_valid_pat(self) -> None:
        result = _check_github_token(verbose=False, config_token="github_pat_abc123")
        assert result.status == "pass"

    def test_invalid_format(self) -> None:
        result = _check_github_token(verbose=False, config_token="bad_token")
        assert result.status == "warn"


class TestCheckAnthropicKey:
    def test_no_key(self) -> None:
        result = _check_anthropic_key(verbose=False, config_key=None)
        assert result.status == "info"

    def test_valid_key(self) -> None:
        result = _check_anthropic_key(verbose=False, config_key="sk-ant-abc123")
        assert result.status == "pass"

    def test_invalid_format(self) -> None:
        result = _check_anthropic_key(verbose=False, config_key="bad_key")
        assert result.status == "warn"


class TestDoctorResults:
    def test_summary(self) -> None:
        results = DoctorResults(
            checks=[
                DoctorCheckResult(1, "a", "pass", "ok"),
                DoctorCheckResult(2, "b", "error", "bad", fix="fix it"),
                DoctorCheckResult(3, "c", "warn", "hmm"),
                DoctorCheckResult(4, "d", "info", "fyi"),
            ]
        )
        assert results.passed == 1
        assert results.errors == 1
        assert results.warnings == 1
        assert results.info == 1
        assert results.has_errors is True


class TestRunDoctorChecks:
    @patch("complyform.cli.doctor_cmd._check_cloud_intelligence_apis", return_value=[])
    @patch("complyform.core.auth.license.LicenseManager")
    def test_community_tier(self, mock_mgr: MagicMock, mock_cloud: MagicMock) -> None:
        mock_mgr.return_value.load_cache.return_value = None
        results = run_doctor_checks(tier="community")
        assert len(results.checks) >= 16

        # Check that network checks are skipped for community
        check_11 = [c for c in results.checks if c.number == 11]
        check_12 = [c for c in results.checks if c.number == 12]
        if check_11:
            assert "skipped" in check_11[0].message
        if check_12:
            assert "skipped" in check_12[0].message


class TestBuildDiagnosticString:
    @patch("complyform.cli.doctor_cmd._run_subprocess", return_value=("3.2.1", "", 0))
    @patch("complyform.cli.doctor_cmd.shutil.which", return_value="/usr/bin/checkov")
    def test_format(self, mock_which: MagicMock, mock_sub: MagicMock) -> None:
        diag = build_diagnostic_string(tier="community", cloud="gcp")
        assert "complyform=" in diag
        assert "python=" in diag
        assert "tier=community" in diag
        assert "cloud=gcp" in diag
        assert "os=" in diag
        assert "cache=" in diag


class TestDoctorExitCode:
    def test_no_errors_exit_0(self) -> None:
        results = DoctorResults(
            checks=[
                DoctorCheckResult(1, "a", "pass", "ok"),
                DoctorCheckResult(2, "b", "info", "fyi"),
            ]
        )
        assert not results.has_errors

    def test_with_errors_exit_2(self) -> None:
        results = DoctorResults(
            checks=[
                DoctorCheckResult(1, "a", "error", "bad", fix="fix"),
            ]
        )
        assert results.has_errors
