"""Tests for complyform config extensions — verbose status, telemetry, corpus-delete."""

from __future__ import annotations

import json
import os
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest
import typer
from rich.console import Console

from complyform.cli.config_cmd import (
    _build_verbose_status,
    config_corpus_delete,
    config_status,
    config_telemetry,
)
from complyform.core.config import ComplyFormConfig


def _make_app_ctx(
    config: ComplyFormConfig | None = None,
    tmp_path: Path | None = None,
) -> MagicMock:
    """Create a mock AppContext."""
    ctx = MagicMock()
    ctx.config = config or ComplyFormConfig()
    ctx.console = Console(stderr=True, quiet=True)
    ctx.config_path = (
        (tmp_path / "config.yaml") if tmp_path else Path("/tmp/config.yaml")
    )
    return ctx


class TestConfigStatusVerbose:
    @patch("complyform.core.auth.license.LicenseManager")
    def test_standard_output(self, mock_mgr_cls: MagicMock) -> None:
        """Standard (non-verbose) output works."""
        mock_mgr_cls.return_value.load_cache.return_value = None
        ctx = MagicMock()
        ctx.obj = _make_app_ctx()
        config_status(ctx, verbose=False, format="terminal")

    @patch("complyform.core.auth.license.LicenseManager")
    def test_verbose_output(self, mock_mgr_cls: MagicMock) -> None:
        """Verbose output includes all sections."""
        mock_mgr_cls.return_value.load_cache.return_value = None
        ctx = MagicMock()
        ctx.obj = _make_app_ctx()
        config_status(ctx, verbose=True, format="terminal")

    @patch("complyform.core.auth.license.LicenseManager")
    def test_verbose_json(
        self,
        mock_mgr_cls: MagicMock,
        capsys: pytest.CaptureFixture[str],
    ) -> None:
        """Verbose JSON output is valid."""
        mock_mgr_cls.return_value.load_cache.return_value = None
        ctx = MagicMock()
        ctx.obj = _make_app_ctx()
        config_status(ctx, verbose=True, format="json")
        captured = capsys.readouterr()
        data = json.loads(captured.out)
        assert data["schema_version"] == 1
        assert "license" in data["result"]
        assert "features" in data["result"]
        assert "profiles" in data["result"]
        assert "cloud_auth" in data["result"]
        assert "cache" in data["result"]

    @patch("complyform.core.auth.license.LicenseManager")
    def test_standard_json(
        self,
        mock_mgr_cls: MagicMock,
        capsys: pytest.CaptureFixture[str],
    ) -> None:
        """Standard JSON output is valid."""
        mock_mgr_cls.return_value.load_cache.return_value = None
        ctx = MagicMock()
        ctx.obj = _make_app_ctx()
        config_status(ctx, verbose=False, format="json")
        captured = capsys.readouterr()
        data = json.loads(captured.out)
        assert data["result"]["tier"] == "community"


class TestBuildVerboseStatus:
    def test_community_tier(self) -> None:
        config = ComplyFormConfig()
        data = _build_verbose_status(
            config=config,
            cache=None,
            tier="community",
            license_status="Community (no license)",
            expires_at=None,
            version="0.1.0",
        )
        assert data["version"] == "0.1.0"
        assert isinstance(data["license"], dict)
        assert isinstance(data["features"], dict)
        assert isinstance(data["profiles"], dict)
        assert isinstance(data["cloud_auth"], dict)
        assert isinstance(data["cache"], dict)

    def test_features_contain_expected_keys(self) -> None:
        config = ComplyFormConfig(tier="team")
        data = _build_verbose_status(
            config=config,
            cache=None,
            tier="team",
            license_status="Active",
            expires_at=None,
            version="0.1.0",
        )
        feats = data["features"]
        assert isinstance(feats, dict)
        assert "html_report" in feats
        assert "iam_analysis" in feats
        assert "scan_sources" in feats


class TestConfigTelemetry:
    def test_enable(self, tmp_path: Path) -> None:
        config = ComplyFormConfig()
        app_ctx = _make_app_ctx(config, tmp_path)
        ctx = MagicMock()
        ctx.obj = app_ctx

        with patch("complyform.core.config.save_config") as mock_save:
            config_telemetry(ctx, enable=True, disable=False, info=False)
            assert config.telemetry is True
            mock_save.assert_called_once()

    def test_disable(self, tmp_path: Path) -> None:
        config = ComplyFormConfig(telemetry=True, corpus_contribution=True)
        app_ctx = _make_app_ctx(config, tmp_path)
        ctx = MagicMock()
        ctx.obj = app_ctx

        with patch("complyform.core.config.save_config") as mock_save:
            config_telemetry(ctx, enable=False, disable=True, info=False)
            assert config.telemetry is False
            assert config.corpus_contribution is False
            mock_save.assert_called_once()

    def test_info(self) -> None:
        config = ComplyFormConfig(telemetry=True)
        app_ctx = _make_app_ctx(config)
        ctx = MagicMock()
        ctx.obj = app_ctx
        # Should not raise
        config_telemetry(ctx, enable=False, disable=False, info=True)

    def test_default_shows_info(self) -> None:
        config = ComplyFormConfig()
        app_ctx = _make_app_ctx(config)
        ctx = MagicMock()
        ctx.obj = app_ctx
        # No flags = info
        config_telemetry(ctx, enable=False, disable=False, info=False)

    @patch.dict(os.environ, {"DO_NOT_TRACK": "1"})
    def test_do_not_track_blocks_enable(self, tmp_path: Path) -> None:
        config = ComplyFormConfig()
        app_ctx = _make_app_ctx(config, tmp_path)
        ctx = MagicMock()
        ctx.obj = app_ctx

        config_telemetry(ctx, enable=True, disable=False, info=False)
        # Telemetry should NOT be enabled
        assert config.telemetry is False

    def test_mutual_exclusivity(self) -> None:
        from complyform.core.errors import MutualExclusivityError

        config = ComplyFormConfig()
        app_ctx = _make_app_ctx(config)
        ctx = MagicMock()
        ctx.obj = app_ctx

        with pytest.raises(MutualExclusivityError):
            config_telemetry(ctx, enable=True, disable=True, info=False)


class TestConfigCorpusDelete:
    def test_no_contributor_id(self) -> None:
        config = ComplyFormConfig()
        app_ctx = _make_app_ctx(config)
        ctx = MagicMock()
        ctx.obj = app_ctx

        with pytest.raises(typer.Exit) as exc_info:
            config_corpus_delete(ctx, confirm=True)
        assert exc_info.value.exit_code == 0

    @patch("httpx.request")
    def test_successful_delete(self, mock_delete: MagicMock, tmp_path: Path) -> None:
        # Use a MagicMock config that returns corpus_contributor_id
        config = MagicMock()
        config.model_dump.return_value = {
            "corpus_contributor_id": "contrib_123",
        }
        config.corpus_contribution = True
        config.telemetry = True

        app_ctx = MagicMock()
        app_ctx.config = config
        app_ctx.console = Console(stderr=True, quiet=True)
        app_ctx.config_path = tmp_path / "config.yaml"

        ctx = MagicMock()
        ctx.obj = app_ctx

        mock_resp = MagicMock()
        mock_resp.status_code = 200
        mock_delete.return_value = mock_resp

        with patch("complyform.core.config.save_config"):
            config_corpus_delete(ctx, confirm=True)
            assert config.corpus_contribution is False

    @patch("httpx.request")
    def test_rate_limited(self, mock_delete: MagicMock, tmp_path: Path) -> None:
        config = MagicMock()
        config.model_dump.return_value = {
            "corpus_contributor_id": "contrib_123",
        }

        app_ctx = MagicMock()
        app_ctx.config = config
        app_ctx.console = Console(stderr=True, quiet=True)
        app_ctx.config_path = tmp_path / "config.yaml"

        ctx = MagicMock()
        ctx.obj = app_ctx

        mock_resp = MagicMock()
        mock_resp.status_code = 429
        mock_delete.return_value = mock_resp

        with pytest.raises(typer.Exit) as exc_info:
            config_corpus_delete(ctx, confirm=True)
        assert exc_info.value.exit_code == 1
