"""
CVE data extraction module.

Provides functions to extract CVE data to CSV format.
"""

import csv
import logging
from pathlib import Path

from .models import CVERecord, _is_cwe_descendant

logger = logging.getLogger(__name__)

# Maximum description length in CSV output
MAX_DESCRIPTION_LENGTH = 200


def extract_cves_to_csv(cves: list[CVERecord], output_file: Path, filter_cwes: set[str] | None = None) -> None:
    """
    Extract CVE records to CSV file with specified format.

    Writes CVE ID, CWE type(s) that matched filter, and truncated description to CSV.
    If filter_cwes provided, only shows CWE types that match the filter.
    CVEs are sorted by ID before writing.

    Args:
        cves: List of CVE records to extract
        output_file: Path to output CSV file
        filter_cwes: Set of CWE types used for filtering (for display purposes)

    Returns:
        None (side effect: writes CSV file)

    Raises:
        ValueError: Empty CVE list
        OSError: File write error
    """
    if not cves:
        raise ValueError("CVE list cannot be empty")

    if not output_file:
        raise ValueError("output_file cannot be None")

    # Sort CVEs by ID
    sorted_cves = sorted(cves, key=lambda cve: cve.cve_id)

    logger.info("Extracting %d CVEs to: %s", len(sorted_cves), output_file)

    # Ensure parent directory exists
    output_file.parent.mkdir(parents=True, exist_ok=True)

    try:
        with open(output_file, "w", encoding="utf-8", newline="") as f:
            writer = csv.writer(f)

            # Write header
            writer.writerow(["CVE ID", "CWE Type", "Description"])

            # Write data
            for cve in sorted_cves:
                cve_id = cve.cve_id
                
                # If filter_cwes provided, show only matching CWE types
                if filter_cwes:
                    matching_cwes = []
                    for cwe in cve.cwe_types:
                        # Check exact match or descendant match
                        if cwe in filter_cwes or any(_is_cwe_descendant(cwe, fcwe) for fcwe in filter_cwes):
                            matching_cwes.append(cwe)
                    cwe_type = ",".join(matching_cwes) if matching_cwes else "Unknown"
                else:
                    cwe_type = cve.cwe_types[0] if cve.cwe_types else "Unknown"
                
                description = _truncate_description(cve.description)
                writer.writerow([cve_id, cwe_type, description])

        logger.info("Successfully extracted %d CVEs to CSV", len(sorted_cves))

    except OSError as e:
        raise OSError(f"Failed to write CSV file {output_file}: {e}") from e


def _truncate_description(description: str) -> str:
    """
    Truncate description to maximum length with ellipsis.

    Args:
        description: Full description text

    Returns:
        Truncated description (max 200 chars)
    """
    if not description:
        return ""

    if len(description) <= MAX_DESCRIPTION_LENGTH:
        return description

    return description[: MAX_DESCRIPTION_LENGTH - 3] + "..."
