"""Tests for core models."""

import json
from pathlib import Path

import pytest

from cve_extractor.core import (
    CommitPatch,
    ComparePatch,
    CVERecord,
    FilePatch,
    PRPatch,
)


@pytest.fixture
def sample_cve_data() -> dict:
    """Sample CVE JSON data."""
    return {
        "cveMetadata": {
            "cveId": "CVE-2024-1234",
            "datePublished": "2024-01-15T00:00:00.000Z",
        },
        "containers": {
            "cna": {
                "problemTypes": [{"descriptions": [{"cweId": "CWE-79", "type": "CWE"}]}],
                "descriptions": [{"value": "Test vulnerability description"}],
                "affected": [{"product": "TestProduct"}],
                "references": [
                    {"url": "https://github.com/test/repo/commit/abc123"},
                ],
            }
        },
    }


@pytest.fixture
def sample_cve_file(sample_cve_data: dict, tmp_path: Path) -> Path:
    """Create temporary CVE JSON file."""
    cve_file = tmp_path / "CVE-2024-1234.json"
    with open(cve_file, "w", encoding="utf-8") as f:
        json.dump(sample_cve_data, f)
    return cve_file


def test_cve_record_load_from_file(sample_cve_file: Path) -> None:
    """Test loading CVE from file."""
    cve = CVERecord.load_from_file(sample_cve_file)

    assert cve.cve_id == "CVE-2024-1234"
    assert "CWE-79" in cve.cwe_types
    assert "Test vulnerability" in cve.description
    assert "TestProduct" in cve.affected_products
    assert len(cve.references) > 0


def test_cve_record_matches_cwe_type() -> None:
    """Test CWE type matching."""
    cve = CVERecord(
        cve_id="CVE-2024-1234",
        cwe_types=["CWE-79"],
        description="XSS vulnerability",
        affected_products=["Product A"],
        references=[],
        published_date="2024-01-15",
    )

    # Exact match
    assert cve.matches_cwe_type({"CWE-79"})

    # No match
    assert not cve.matches_cwe_type({"CWE-89"})

    # Empty set
    assert not cve.matches_cwe_type(set())


def test_cve_record_matches_keywords() -> None:
    """Test keyword matching."""
    cve = CVERecord(
        cve_id="CVE-2024-1234",
        cwe_types=["CWE-79"],
        description="SQL injection vulnerability in database",
        affected_products=["MySQL Plugin"],
        references=[],
        published_date="2024-01-15",
    )

    # Match in description
    assert cve.matches_keywords({"SQL"})
    assert cve.matches_keywords({"database"})

    # Match in products (case-insensitive)
    assert cve.matches_keywords({"mysql"})

    # No match
    assert not cve.matches_keywords({"PHP"})

    # Empty set
    assert not cve.matches_keywords(set())


def test_commit_patch() -> None:
    """Test CommitPatch class."""
    patch = CommitPatch(
        cve_id="CVE-2024-1234",
        repository="owner/repo",
        commit_sha="abc123def456",
    )

    assert patch.get_patch_commit_sha() == "abc123def456"
    assert patch.get_patch_files() == []


def test_file_patch() -> None:
    """Test FilePatch class."""
    patch = FilePatch(
        cve_id="CVE-2024-1234",
        repository="owner/repo",
        commit_sha="abc123def456",
        file_path="src/main.php",
    )

    assert patch.get_patch_commit_sha() == "abc123def456"
    assert patch.get_patch_files() == ["src/main.php"]


def test_pr_patch() -> None:
    """Test PRPatch class."""
    patch = PRPatch(
        cve_id="CVE-2024-1234",
        repository="owner/repo",
        pr_number=123,
        merge_commit_sha="abc123def456",
    )

    assert patch.get_patch_commit_sha() == "abc123def456"
    assert patch.get_patch_files() == []


def test_pr_patch_no_merge_sha() -> None:
    """Test PRPatch without merge commit SHA."""
    patch = PRPatch(
        cve_id="CVE-2024-1234",
        repository="owner/repo",
        pr_number=123,
        merge_commit_sha=None,
    )

    with pytest.raises(ValueError, match="no merge_commit_sha"):
        patch.get_patch_commit_sha()


def test_compare_patch() -> None:
    """Test ComparePatch class."""
    patch = ComparePatch(
        cve_id="CVE-2024-1234",
        repository="owner/repo",
        base_sha="abc123",
        head_sha="def456",
    )

    assert patch.get_patch_commit_sha() == "def456"
    assert patch.get_patch_files() == []
