"""Tests for aicert_pro.cli module."""

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

import pytest
from typer.testing import CliRunner

from aicert_pro.cli import app, run_aicert


runner = CliRunner()


# Sample valid license for testing
SAMPLE_LICENSE = {
    "license_id": "test-license-123",
    "owner": "test@example.com",
    "issued_at": "2024-01-01T00:00:00+00:00",
    "expires_at": None,
    "features": ["baseline", "advanced"],
    "signature": "test-signature",
}


class TestRunAicert:
    """Tests for run_aicert function."""

    def test_run_aicert_success_with_output(self, tmp_path, monkeypatch):
        """Test run_aicert when subprocess succeeds with path in output."""
        config_path = tmp_path / "aicert.yaml"
        config_path.write_text("provider: anthropic\nmodel: claude-3-5-sonnet")
        
        run_dir = tmp_path / "runs" / "test-run"
        run_dir.mkdir(parents=True)
        (run_dir / "summary.json").write_text("{}")
        
        mock_result = MagicMock()
        mock_result.returncode = 0
        mock_result.stdout = f"Run complete. Results in: {run_dir}"
        mock_result.stderr = ""
        
        with patch("subprocess.run", return_value=mock_result) as mock_run:
            result = run_aicert(config_path)
            assert result == run_dir
            mock_run.assert_called_once()
            args = mock_run.call_args[0][0]
            assert "aicert" in args
            assert "stability" in args
            assert str(config_path) in args
            assert "--ci" in args

    def test_run_aicert_failure_propagates_exit_code(self, tmp_path, monkeypatch):
        """Test that subprocess failure exit code is propagated."""
        config_path = tmp_path / "aicert.yaml"
        config_path.write_text("provider: anthropic\nmodel: claude-3-5-sonnet")
        
        mock_result = MagicMock()
        mock_result.returncode = 42
        mock_result.stdout = ""
        mock_result.stderr = "aicert error: something went wrong"
        
        with patch("subprocess.run", return_value=mock_result):
            with runner.isolated_filesystem():
                # Copy config file to isolated filesystem
                import shutil
                shutil.copy(config_path, "aicert.yaml")
                
                with patch("aicert_pro.cli.require_pro", return_value=MagicMock()):
                    result = runner.invoke(app, ["baseline", "save", "aicert.yaml"])
                    assert result.exit_code == 42

    def test_run_aicert_extracts_path_from_any_line(self, tmp_path, monkeypatch):
        """Test run_aicert extracts path from any line containing 'Results in'."""
        config_path = tmp_path / "aicert.yaml"
        config_path.write_text("provider: anthropic\nmodel: claude-3-5-sonnet")
        
        run_dir = tmp_path / "my-run"
        run_dir.mkdir(parents=True)
        (run_dir / "summary.json").write_text("{}")
        
        mock_result = MagicMock()
        mock_result.returncode = 0
        mock_result.stdout = f"""Starting aicert stability run...
Processing test cases...
Evaluation complete. Results in: {run_dir}
Run finished successfully."""
        mock_result.stderr = ""
        
        with patch("subprocess.run", return_value=mock_result):
            result = run_aicert(config_path)
            assert result == run_dir


class TestBaselineSave:
    """Tests for baseline save command."""

    def test_save_with_yaml_file(self, tmp_path, monkeypatch):
        """Test save command with .yaml file runs aicert first."""
        # Setup config and run directory
        config_path = tmp_path / "aicert.yaml"
        config_path.write_text("provider: anthropic\nmodel: claude-3-5-sonnet")
        
        run_dir = tmp_path / "runs" / "test-run"
        run_dir.mkdir(parents=True)
        summary = {
            "project": "test-project",
            "provider": "anthropic",
            "model": "claude-3-5-sonnet",
            "temperature": 0.5,
            "timestamp": "2024-01-15T10:00:00Z",
            "metrics": {
                "stability": 92.5,
                "compliance": 97.0,
                "p95_latency_ms": 2000,
            },
        }
        with open(run_dir / "summary.json", "w") as f:
            json.dump(summary, f)
        
        mock_result = MagicMock()
        mock_result.returncode = 0
        mock_result.stdout = f"Results in: {run_dir}"
        mock_result.stderr = ""
        
        with patch("subprocess.run", return_value=mock_result):
            with patch("aicert_pro.cli.require_pro", return_value=MagicMock()):
                result = runner.invoke(
                    app,
                    ["baseline", "save", str(config_path), "--baseline-dir", str(tmp_path / "baselines")],
                )
                assert result.exit_code == 0
                assert "Baseline saved to" in result.output
                assert (tmp_path / "baselines" / "test-project.json").exists()

    def test_save_with_directory(self, tmp_path, monkeypatch):
        """Test save command with directory directly uses it."""
        run_dir = tmp_path / "my-run"
        run_dir.mkdir()
        summary = {
            "project": "dir-project",
            "provider": "anthropic",
            "model": "claude-3-5-sonnet",
            "temperature": 0.5,
            "timestamp": "2024-01-15T10:00:00Z",
            "metrics": {
                "stability": 92.5,
                "compliance": 97.0,
            },
        }
        with open(run_dir / "summary.json", "w") as f:
            json.dump(summary, f)
        
        with patch("subprocess.run") as mock_run:
            with patch("aicert_pro.cli.require_pro", return_value=MagicMock()):
                result = runner.invoke(
                    app,
                    ["baseline", "save", str(run_dir), "--baseline-dir", str(tmp_path / "baselines")],
                )
                assert result.exit_code == 0
                # subprocess should not be called for directory
                mock_run.assert_not_called()
                assert (tmp_path / "baselines" / "dir-project.json").exists()


class TestBaselineCheck:
    """Tests for baseline check command."""

    def test_check_with_yaml_file(self, tmp_path, monkeypatch):
        """Test check command with .yaml file runs aicert first."""
        # Setup config and run directory
        config_path = tmp_path / "aicert.yaml"
        config_path.write_text("provider: anthropic\nmodel: claude-3-5-sonnet")
        
        run_dir = tmp_path / "runs" / "test-run"
        run_dir.mkdir(parents=True)
        summary = {
            "project": "test-project",
            "provider": "anthropic",
            "model": "claude-3-5-sonnet",
            "temperature": 0.5,
            "timestamp": "2024-01-15T10:00:00Z",
            "metrics": {
                "stability": 94.0,  # 1 point drop from baseline (within 5)
                "compliance": 97.0,
                "p95_latency_ms": 1000,
            },
        }
        with open(run_dir / "summary.json", "w") as f:
            json.dump(summary, f)
        
        # Create baseline
        baseline = {
            "project": "test-project",
            "metrics": {
                "stability": 95.0,
                "compliance": 98.0,
                "p95_latency_ms": 1000,
            }
        }
        baseline_file = tmp_path / "baselines" / "test-project.json"
        baseline_file.parent.mkdir(parents=True)
        with open(baseline_file, "w") as f:
            json.dump(baseline, f)
        
        mock_result = MagicMock()
        mock_result.returncode = 0
        mock_result.stdout = f"Results in: {run_dir}"
        mock_result.stderr = ""
        
        with patch("subprocess.run", return_value=mock_result):
            with patch("aicert_pro.cli.require_pro", return_value=MagicMock()):
                result = runner.invoke(
                    app,
                    ["baseline", "check", str(config_path), "--baseline-dir", str(tmp_path / "baselines")],
                )
                assert result.exit_code == 0
                assert "Baseline check passed" in result.output

    def test_check_with_yaml_file_regression_fails_exit_2(self, tmp_path, monkeypatch):
        """Test check command exits with code 2 on regression failure."""
        config_path = tmp_path / "aicert.yaml"
        config_path.write_text("provider: anthropic\nmodel: claude-3-5-sonnet")
        
        run_dir = tmp_path / "runs" / "test-run"
        run_dir.mkdir(parents=True)
        summary = {
            "project": "test-project",
            "provider": "anthropic",
            "model": "claude-3-5-sonnet",
            "temperature": 0.5,
            "timestamp": "2024-01-15T10:00:00Z",
            "metrics": {
                "stability": 85.0,  # 10 point drop (exceeds 5)
                "compliance": 97.0,
                "p95_latency_ms": 1000,
            },
        }
        with open(run_dir / "summary.json", "w") as f:
            json.dump(summary, f)
        
        # Create baseline
        baseline = {
            "project": "test-project",
            "metrics": {
                "stability": 95.0,
                "compliance": 98.0,
                "p95_latency_ms": 1000,
            }
        }
        baseline_file = tmp_path / "baselines" / "test-project.json"
        baseline_file.parent.mkdir(parents=True)
        with open(baseline_file, "w") as f:
            json.dump(baseline, f)
        
        mock_result = MagicMock()
        mock_result.returncode = 0
        mock_result.stdout = f"Results in: {run_dir}"
        mock_result.stderr = ""
        
        with patch("subprocess.run", return_value=mock_result):
            with patch("aicert_pro.cli.require_pro", return_value=MagicMock()):
                result = runner.invoke(
                    app,
                    ["baseline", "check", str(config_path), "--baseline-dir", str(tmp_path / "baselines")],
                )
                assert result.exit_code == 2
                assert "Baseline regression detected" in result.output

    def test_check_with_directory(self, tmp_path, monkeypatch):
        """Test check command with directory directly uses it."""
        run_dir = tmp_path / "my-run"
        run_dir.mkdir()
        summary = {
            "project": "dir-project",
            "provider": "anthropic",
            "model": "claude-3-5-sonnet",
            "temperature": 0.5,
            "timestamp": "2024-01-15T10:00:00Z",
            "metrics": {
                "stability": 94.0,  # 1 point drop (within 5)
                "compliance": 97.0,
            },
        }
        with open(run_dir / "summary.json", "w") as f:
            json.dump(summary, f)
        
        # Create baseline
        baseline = {
            "project": "dir-project",
            "metrics": {
                "stability": 95.0,
                "compliance": 98.0,
            }
        }
        baseline_file = tmp_path / "baselines" / "dir-project.json"
        baseline_file.parent.mkdir(parents=True)
        with open(baseline_file, "w") as f:
            json.dump(baseline, f)
        
        with patch("subprocess.run") as mock_run:
            with patch("aicert_pro.cli.require_pro", return_value=MagicMock()):
                result = runner.invoke(
                    app,
                    ["baseline", "check", str(run_dir), "--baseline-dir", str(tmp_path / "baselines")],
                )
                assert result.exit_code == 0
                # subprocess should not be called for directory
                mock_run.assert_not_called()

    def test_check_with_yml_extension(self, tmp_path, monkeypatch):
        """Test check command works with .yml extension."""
        config_path = tmp_path / "aicert.yml"
        config_path.write_text("provider: anthropic\nmodel: claude-3-5-sonnet")
        
        run_dir = tmp_path / "runs" / "test-run"
        run_dir.mkdir(parents=True)
        summary = {
            "project": "yml-project",
            "provider": "anthropic",
            "model": "claude-3-5-sonnet",
            "temperature": 0.5,
            "timestamp": "2024-01-15T10:00:00Z",
            "metrics": {
                "stability": 94.0,
                "compliance": 97.0,
            },
        }
        with open(run_dir / "summary.json", "w") as f:
            json.dump(summary, f)
        
        # Create baseline
        baseline = {
            "project": "yml-project",
            "metrics": {
                "stability": 95.0,
                "compliance": 98.0,
            }
        }
        baseline_file = tmp_path / "baselines" / "yml-project.json"
        baseline_file.parent.mkdir(parents=True)
        with open(baseline_file, "w") as f:
            json.dump(baseline, f)
        
        mock_result = MagicMock()
        mock_result.returncode = 0
        mock_result.stdout = f"Results in: {run_dir}"
        mock_result.stderr = ""
        
        with patch("subprocess.run", return_value=mock_result):
            with patch("aicert_pro.cli.require_pro", return_value=MagicMock()):
                result = runner.invoke(
                    app,
                    ["baseline", "check", str(config_path), "--baseline-dir", str(tmp_path / "baselines")],
                )
                assert result.exit_code == 0


class TestExitCodes:
    """Tests for exit code propagation."""

    def test_license_error_exits_5(self, tmp_path, monkeypatch):
        """Test that license errors result in exit code 5."""
        from aicert_pro.license import LicenseMissingError
        # Remove license env var
        monkeypatch.delenv("AICERT_PRO_LICENSE", raising=False)
        
        with patch("subprocess.run"):
            with patch("aicert_pro.cli.require_pro", side_effect=LicenseMissingError()):
                result = runner.invoke(app, ["baseline", "save", str(tmp_path / "run")])
                assert result.exit_code == 5

    def test_aicert_failure_exits_with_same_code(self, tmp_path, monkeypatch):
        """Test that aicert subprocess failure exits with same code."""
        config_path = tmp_path / "aicert.yaml"
        config_path.write_text("provider: anthropic")
        
        run_dir = tmp_path / "runs" / "test"
        run_dir.mkdir(parents=True)
        (run_dir / "summary.json").write_text("{}")
        
        mock_result = MagicMock()
        mock_result.returncode = 7
        mock_result.stdout = ""
        mock_result.stderr = "Error: aicert failed"
        
        with patch("subprocess.run", return_value=mock_result):
            with patch("aicert_pro.cli.require_pro", return_value=MagicMock()):
                result = runner.invoke(app, ["baseline", "save", str(config_path)])
                assert result.exit_code == 7
