"""Unit tests for ExportSarifTool."""

import json
import tempfile
from datetime import datetime
from pathlib import Path
from unittest.mock import MagicMock

import pytest

from tools.export_sarif.models import SarifExportError, SarifExportResponse
from tools.export_sarif.tool import ExportSarifTool
from tools.generate_report.models import AuditLens
from tools.workflow.state import WorkflowState


@pytest.fixture
def tool():
    return ExportSarifTool()


@pytest.fixture
def mock_consolidated():
    mock = MagicMock()
    mock.files_checked = {"src/auth.py", "src/api.py"}
    mock.issues_found = [
        {
            "severity": "critical",
            "category": "SQL Injection",
            "description": "SQL injection vulnerability",
            "affected_files": ["src/api.py"],
            "remediation": "Use parameterized queries",
            "cwe_id": "CWE-89",
        },
        {
            "severity": "high",
            "category": "XSS",
            "description": "Cross-site scripting vulnerability",
            "affected_files": ["src/views.py"],
            "cwe_id": "CWE-79",
        },
    ]
    return mock


@pytest.fixture
def finished_workflow_state(mock_consolidated):
    state = WorkflowState(
        continuation_id="test-uuid-finished",
        tool_name="security_audit",
    )
    state.consolidated = mock_consolidated
    state.is_finished = True
    return state


@pytest.fixture
def unfinished_workflow_state(mock_consolidated):
    state = WorkflowState(
        continuation_id="test-uuid-unfinished",
        tool_name="security_audit",
    )
    state.consolidated = mock_consolidated
    state.is_finished = False
    return state


class TestExportSarifTool:
    """Tests for ExportSarifTool."""

    def test_name_property(self, tool):
        assert tool.name == "export_sarif"

    def test_description_property(self, tool):
        assert "SARIF" in tool.description
        assert "2.1.0" in tool.description

    def test_execute_requires_finished_workflow(self, tool, unfinished_workflow_state):
        tool._state_manager._workflows = {"test-uuid": unfinished_workflow_state}

        result = tool.execute(continuation_id="test-uuid")

        assert isinstance(result, SarifExportError)
        assert result.error_type == "WorkflowIncomplete"

    def test_execute_exports_sarif_for_finished_workflow(
        self, tool, finished_workflow_state
    ):
        with tempfile.TemporaryDirectory() as tmp_dir:
            finished_workflow_state.project_root_path = tmp_dir
            tool._state_manager._workflows = {
                "test-uuid-finished": finished_workflow_state
            }

            result = tool.execute(continuation_id="test-uuid-finished")

            assert isinstance(result, SarifExportResponse)
            assert result.success is True
            assert ".optix" in str(result.sarif_file_path)
            assert "SARIF_SECURITY" in str(result.sarif_file_path)
            assert result.lens == AuditLens.SECURITY
            assert result.findings_count == 2
            assert result.rules_count == 2

    def test_execute_creates_sarif_file(self, tool, finished_workflow_state):
        with tempfile.TemporaryDirectory() as tmp_dir:
            finished_workflow_state.project_root_path = tmp_dir
            tool._state_manager._workflows = {
                "test-uuid-finished": finished_workflow_state
            }

            result = tool.execute(continuation_id="test-uuid-finished")

            assert result.success is True
            assert Path(result.sarif_file_path).exists()

            content = Path(result.sarif_file_path).read_text()
            sarif_data = json.loads(content)
            assert sarif_data["version"] == "2.1.0"
            assert "$schema" in sarif_data

    def test_execute_sarif_content_valid(self, tool, finished_workflow_state):
        with tempfile.TemporaryDirectory() as tmp_dir:
            finished_workflow_state.project_root_path = tmp_dir
            tool._state_manager._workflows = {
                "test-uuid-finished": finished_workflow_state
            }

            result = tool.execute(continuation_id="test-uuid-finished")

            content = Path(result.sarif_file_path).read_text()
            sarif_data = json.loads(content)

            assert len(sarif_data["runs"]) == 1
            assert len(sarif_data["runs"][0]["results"]) == 2
            assert sarif_data["runs"][0]["tool"]["driver"]["name"] == "Optix"

    def test_execute_uses_most_recent_finished(self, tool, mock_consolidated):
        state1 = WorkflowState(
            continuation_id="uuid-1",
            tool_name="security_audit",
        )
        state1.consolidated = mock_consolidated
        state1.is_finished = True
        state1.updated_at = datetime(2026, 1, 1, 10, 0, 0)

        state2 = WorkflowState(
            continuation_id="uuid-2",
            tool_name="a11y_audit",
        )
        state2.consolidated = mock_consolidated
        state2.is_finished = True
        state2.updated_at = datetime(2026, 1, 2, 10, 0, 0)

        with tempfile.TemporaryDirectory() as tmp_dir:
            state1.project_root_path = tmp_dir
            state2.project_root_path = tmp_dir
            tool._state_manager._workflows = {
                "uuid-1": state1,
                "uuid-2": state2,
            }

            result = tool.execute()

            assert result.success is True
            assert result.lens == AuditLens.A11Y

    def test_execute_no_audit_found_error(self, tool):
        tool._state_manager._workflows = {}

        result = tool.execute()

        assert isinstance(result, SarifExportError)
        assert "No completed audit found" in result.error
        assert result.error_type == "NoAuditFound"

    def test_execute_invalid_continuation_id_error(self, tool):
        tool._state_manager._workflows = {}

        result = tool.execute(continuation_id="nonexistent-uuid")

        assert isinstance(result, SarifExportError)
        assert "nonexistent-uuid" in result.error
        assert result.error_type == "NoAuditFound"

    def test_execute_with_custom_output_path(self, tool, finished_workflow_state):
        with tempfile.TemporaryDirectory() as tmp_dir:
            finished_workflow_state.project_root_path = tmp_dir
            tool._state_manager._workflows = {
                "test-uuid-finished": finished_workflow_state
            }
            custom_path = Path(tmp_dir) / "custom" / "output.sarif.json"

            result = tool.execute(
                continuation_id="test-uuid-finished",
                output_path=str(custom_path),
            )

            assert result.success is True
            assert result.sarif_file_path == custom_path
            assert custom_path.exists()


class TestExportSarifToolWithDifferentLenses:
    """Tests for exporting SARIF with different lens types."""

    @pytest.fixture
    def base_mock_consolidated(self):
        mock = MagicMock()
        mock.files_checked = set()
        mock.issues_found = []
        return mock

    def test_security_lens_export(self, tool, base_mock_consolidated):
        state = WorkflowState(continuation_id="test", tool_name="security_audit")
        state.consolidated = base_mock_consolidated
        state.is_finished = True

        with tempfile.TemporaryDirectory() as tmp_dir:
            state.project_root_path = tmp_dir
            tool._state_manager._workflows = {"test": state}

            result = tool.execute(continuation_id="test")

            assert result.success
            assert result.lens == AuditLens.SECURITY
            assert ".optix" in str(result.sarif_file_path)
            assert "SARIF_SECURITY" in str(result.sarif_file_path)

    def test_a11y_lens_export(self, tool, base_mock_consolidated):
        state = WorkflowState(continuation_id="test", tool_name="a11y_audit")
        state.consolidated = base_mock_consolidated
        state.is_finished = True

        with tempfile.TemporaryDirectory() as tmp_dir:
            state.project_root_path = tmp_dir
            tool._state_manager._workflows = {"test": state}

            result = tool.execute(continuation_id="test")

            assert result.success
            assert result.lens == AuditLens.A11Y
            assert "SARIF_A11Y" in str(result.sarif_file_path)

    def test_devops_lens_export(self, tool, base_mock_consolidated):
        state = WorkflowState(continuation_id="test", tool_name="devops_audit")
        state.consolidated = base_mock_consolidated
        state.is_finished = True

        with tempfile.TemporaryDirectory() as tmp_dir:
            state.project_root_path = tmp_dir
            tool._state_manager._workflows = {"test": state}

            result = tool.execute(continuation_id="test")

            assert result.success
            assert result.lens == AuditLens.DEVOPS
            assert "SARIF_DEVOPS" in str(result.sarif_file_path)

    def test_principal_lens_export(self, tool, base_mock_consolidated):
        state = WorkflowState(continuation_id="test", tool_name="principal_audit")
        state.consolidated = base_mock_consolidated
        state.is_finished = True

        with tempfile.TemporaryDirectory() as tmp_dir:
            state.project_root_path = tmp_dir
            tool._state_manager._workflows = {"test": state}

            result = tool.execute(continuation_id="test")

            assert result.success
            assert result.lens == AuditLens.PRINCIPAL
            assert "SARIF_PRINCIPAL" in str(result.sarif_file_path)


class TestExportSarifToolNumbering:
    """Tests for SARIF file numbering."""

    @pytest.fixture
    def mock_consolidated(self):
        mock = MagicMock()
        mock.issues_found = []
        return mock

    def test_increments_file_number(self, tool, mock_consolidated):
        state = WorkflowState(continuation_id="test", tool_name="security_audit")
        state.consolidated = mock_consolidated
        state.is_finished = True

        with tempfile.TemporaryDirectory() as tmp_dir:
            state.project_root_path = tmp_dir
            tool._state_manager._workflows = {"test": state}

            result1 = tool.execute(continuation_id="test")
            result2 = tool.execute(continuation_id="test")

            assert "SARIF_SECURITY_1.sarif.json" in str(result1.sarif_file_path)
            assert "SARIF_SECURITY_2.sarif.json" in str(result2.sarif_file_path)

    def test_different_lenses_have_separate_numbering(self, tool, mock_consolidated):
        security_state = WorkflowState(continuation_id="sec", tool_name="security_audit")
        security_state.consolidated = mock_consolidated
        security_state.is_finished = True

        a11y_state = WorkflowState(continuation_id="a11y", tool_name="a11y_audit")
        a11y_state.consolidated = mock_consolidated
        a11y_state.is_finished = True

        with tempfile.TemporaryDirectory() as tmp_dir:
            security_state.project_root_path = tmp_dir
            a11y_state.project_root_path = tmp_dir
            tool._state_manager._workflows = {
                "sec": security_state,
                "a11y": a11y_state,
            }

            result1 = tool.execute(continuation_id="sec")
            result2 = tool.execute(continuation_id="a11y")

            assert "SARIF_SECURITY_1.sarif.json" in str(result1.sarif_file_path)
            assert "SARIF_A11Y_1.sarif.json" in str(result2.sarif_file_path)
