"""Tests for requirements traceability matrix generation."""
from abi_spec.traceability import (
    generate_req_id,
    generate_requirements_index,
    generate_traceability_matrix,
    validate_req_id,
)


class TestReqIdGeneration:
    def test_generate_req_id_basic(self):
        assert generate_req_id(0) == "REQ-001"
        assert generate_req_id(1) == "REQ-002"
        assert generate_req_id(42) == "REQ-043"

    def test_generate_req_id_large_numbers(self):
        assert generate_req_id(98) == "REQ-099"
        assert generate_req_id(99) == "REQ-100"
        assert generate_req_id(999) == "REQ-1000"

    def test_validate_req_id_valid(self):
        assert validate_req_id("REQ-001") is True
        assert validate_req_id("REQ-999") is True
        assert validate_req_id("REQ-1000") is True

    def test_validate_req_id_invalid_format(self):
        import pytest

        with pytest.raises(ValueError, match="REQ-ID must match pattern"):
            validate_req_id("req-001")  # lowercase

        with pytest.raises(ValueError, match="REQ-ID must match pattern"):
            validate_req_id("REQ-01")  # too short

        with pytest.raises(ValueError, match="REQ-ID must match pattern"):
            validate_req_id("REQ-ABC")  # not numeric

        with pytest.raises(ValueError, match="REQ-ID must match pattern"):
            validate_req_id("R-001")  # wrong prefix


class TestRequirementsIndex:
    def test_generate_requirements_index_basic(self):
        requirements = [
            {
                "id": "REQ-001",
                "title": "Authentication",
                "text": "System shall authenticate users",
                "acceptance": [
                    "Given valid token, When request made, Then access granted"
                ],
                "source_prd_section": "Security",
            }
        ]

        index = generate_requirements_index(requirements)

        assert len(index) == 1
        assert index[0]["req_id"] == "REQ-001"
        assert index[0]["title"] == "Authentication"
        assert index[0]["source_prd_section"] == "Security"
        assert "acceptance_criteria" in index[0]
        assert "given" in index[0]["acceptance_criteria"]
        assert "when" in index[0]["acceptance_criteria"]
        assert "then" in index[0]["acceptance_criteria"]

    def test_generate_requirements_index_fallback_title(self):
        """Test that title falls back to text if not provided."""
        requirements = [
            {
                "id": "REQ-001",
                "text": "This is a long requirement text that should be truncated",
                "acceptance": ["Test passes"],
            }
        ]

        index = generate_requirements_index(requirements)

        assert len(index) == 1
        assert len(index[0]["title"]) <= 50
        assert index[0]["title"].startswith("This is a long requirement")

    def test_generate_requirements_index_default_source_section(self):
        """Test that source_prd_section defaults to 'Unspecified'."""
        requirements = [
            {
                "id": "REQ-001",
                "title": "Test",
                "text": "Test requirement",
                "acceptance": ["Test passes"],
            }
        ]

        index = generate_requirements_index(requirements)

        assert index[0]["source_prd_section"] == "Unspecified"

    def test_generate_requirements_index_multiple_requirements(self):
        requirements = [
            {
                "id": "REQ-001",
                "title": "Auth",
                "text": "Authentication",
                "acceptance": ["Given token, When request, Then access"],
                "source_prd_section": "Security",
            },
            {
                "id": "REQ-002",
                "title": "Logging",
                "text": "API logging",
                "acceptance": ["Given request, When completed, Then logged"],
                "source_prd_section": "Observability",
            },
        ]

        index = generate_requirements_index(requirements)

        assert len(index) == 2
        assert index[0]["req_id"] == "REQ-001"
        assert index[1]["req_id"] == "REQ-002"


class TestTraceabilityMatrix:
    def test_generate_traceability_matrix_no_tests(self):
        requirements_index = [
            {
                "req_id": "REQ-001",
                "title": "Authentication",
                "acceptance_criteria": {
                    "given": ["valid token"],
                    "when": ["request made"],
                    "then": ["access granted"],
                },
                "source_prd_section": "Security",
            }
        ]

        matrix = generate_traceability_matrix(requirements_index)

        assert matrix["matrix_version"] == "1.0"
        assert len(matrix["requirements"]) == 1
        assert matrix["requirements"][0]["req_id"] == "REQ-001"
        assert matrix["requirements"][0]["coverage_status"] == "uncovered"
        assert matrix["requirements"][0]["verification_status"] == "untested"
        assert matrix["requirements"][0]["tests"] == []

        summary = matrix["summary"]
        assert summary["total_requirements"] == 1
        assert summary["covered_requirements"] == 0
        assert summary["uncovered_requirements"] == 1

    def test_generate_traceability_matrix_with_passing_tests(self):
        requirements_index = [
            {
                "req_id": "REQ-001",
                "title": "Authentication",
                "acceptance_criteria": {
                    "given": ["valid token"],
                    "when": ["request made"],
                    "then": ["access granted"],
                },
                "source_prd_section": "Security",
            }
        ]

        test_results = {
            "test_auth_valid_token": {
                "req_ids": ["REQ-001"],
                "status": "passed",
            }
        }

        matrix = generate_traceability_matrix(requirements_index, test_results)

        assert len(matrix["requirements"]) == 1
        req = matrix["requirements"][0]
        assert req["coverage_status"] == "covered"
        assert req["verification_status"] == "passed"
        assert len(req["tests"]) == 1
        assert req["tests"][0]["test_id"] == "test_auth_valid_token"
        assert req["tests"][0]["status"] == "passed"

        summary = matrix["summary"]
        assert summary["covered_requirements"] == 1
        assert summary["passed_requirements"] == 1
        assert summary["failed_requirements"] == 0

    def test_generate_traceability_matrix_with_failing_tests(self):
        requirements_index = [
            {
                "req_id": "REQ-001",
                "title": "Authentication",
                "acceptance_criteria": {
                    "given": ["valid token"],
                    "when": ["request made"],
                    "then": ["access granted"],
                },
                "source_prd_section": "Security",
            }
        ]

        test_results = {
            "test_auth_valid": {"req_ids": ["REQ-001"], "status": "passed"},
            "test_auth_expired": {"req_ids": ["REQ-001"], "status": "failed"},
        }

        matrix = generate_traceability_matrix(requirements_index, test_results)

        req = matrix["requirements"][0]
        assert req["coverage_status"] == "covered"
        assert req["verification_status"] == "failed"
        assert len(req["tests"]) == 2

        summary = matrix["summary"]
        assert summary["failed_requirements"] == 1
        assert summary["passed_requirements"] == 0

    def test_generate_traceability_matrix_with_pending_tests(self):
        requirements_index = [
            {
                "req_id": "REQ-001",
                "title": "Authentication",
                "acceptance_criteria": {
                    "given": ["valid token"],
                    "when": ["request made"],
                    "then": ["access granted"],
                },
                "source_prd_section": "Security",
            }
        ]

        test_results = {
            "test_auth_valid": {"req_ids": ["REQ-001"], "status": "passed"},
            "test_auth_pending": {"req_ids": ["REQ-001"], "status": "pending"},
        }

        matrix = generate_traceability_matrix(requirements_index, test_results)

        req = matrix["requirements"][0]
        assert req["verification_status"] == "pending"

        summary = matrix["summary"]
        assert summary["pending_requirements"] == 1

    def test_generate_traceability_matrix_multiple_requirements(self):
        requirements_index = [
            {
                "req_id": "REQ-001",
                "title": "Auth",
                "acceptance_criteria": {"given": [], "when": [], "then": []},
                "source_prd_section": "Security",
            },
            {
                "req_id": "REQ-002",
                "title": "Logging",
                "acceptance_criteria": {"given": [], "when": [], "then": []},
                "source_prd_section": "Observability",
            },
            {
                "req_id": "REQ-003",
                "title": "Metrics",
                "acceptance_criteria": {"given": [], "when": [], "then": []},
                "source_prd_section": "Observability",
            },
        ]

        test_results = {
            "test_auth": {"req_ids": ["REQ-001"], "status": "passed"},
            "test_logging": {"req_ids": ["REQ-002"], "status": "failed"},
            # REQ-003 has no tests
        }

        matrix = generate_traceability_matrix(requirements_index, test_results)

        assert len(matrix["requirements"]) == 3

        summary = matrix["summary"]
        assert summary["total_requirements"] == 3
        assert summary["covered_requirements"] == 2
        assert summary["uncovered_requirements"] == 1
        assert summary["passed_requirements"] == 1
        assert summary["failed_requirements"] == 1
        assert summary["pending_requirements"] == 0

    def test_generate_traceability_matrix_one_test_multiple_requirements(self):
        """Test that one test can verify multiple requirements."""
        requirements_index = [
            {
                "req_id": "REQ-001",
                "title": "Auth",
                "acceptance_criteria": {"given": [], "when": [], "then": []},
                "source_prd_section": "Security",
            },
            {
                "req_id": "REQ-002",
                "title": "Logging",
                "acceptance_criteria": {"given": [], "when": [], "then": []},
                "source_prd_section": "Observability",
            },
        ]

        test_results = {
            "test_auth_with_logging": {
                "req_ids": ["REQ-001", "REQ-002"],
                "status": "passed",
            }
        }

        matrix = generate_traceability_matrix(requirements_index, test_results)

        assert len(matrix["requirements"]) == 2
        assert matrix["requirements"][0]["coverage_status"] == "covered"
        assert matrix["requirements"][1]["coverage_status"] == "covered"
        assert matrix["requirements"][0]["tests"][0]["test_id"] == "test_auth_with_logging"
        assert matrix["requirements"][1]["tests"][0]["test_id"] == "test_auth_with_logging"

        summary = matrix["summary"]
        assert summary["covered_requirements"] == 2
        assert summary["passed_requirements"] == 2
