from uv_secure.configuration import Configuration
from uv_secure.dependency_checker.severity import extract_vulnerability_severity
from uv_secure.package_info import PackageInfo, Vulnerability


def filter_vulnerabilities(package: PackageInfo, config: Configuration) -> set[str]:
    """Filter vulnerabilities and return used ignore identifiers.

    Returns:
        set[str]: Ignore IDs that matched vulnerabilities in this package.
    """

    ignore_vulnerabilities = config.vulnerability_criteria.ignore_vulnerabilities
    severity_threshold = config.vulnerability_criteria.severity
    used_ignore_ids: set[str] = set()
    filtered_vulnerabilities: list[Vulnerability] = []
    for vuln in package.vulnerabilities:
        if vuln.withdrawn is not None:
            continue

        matched_ids = matched_ignore_ids(vuln, ignore_vulnerabilities)
        if matched_ids:
            used_ignore_ids.update(matched_ids)
            continue

        if is_unfixed(vuln) and config.vulnerability_criteria.ignore_unfixed:
            continue

        vulnerability_severity = extract_vulnerability_severity(vuln)
        if (
            vulnerability_severity is not None
            and vulnerability_severity.rank < severity_threshold.rank
        ):
            continue

        filtered_vulnerabilities.append(vuln)

    package.vulnerabilities = filtered_vulnerabilities
    return used_ignore_ids


def matched_ignore_ids(
    vulnerability: Vulnerability, ignore_vulnerabilities: set[str] | None
) -> set[str]:
    """Return configured ignore IDs that match a vulnerability."""

    if ignore_vulnerabilities is None:
        return set()

    matched_ids: set[str] = set()
    if vulnerability.id in ignore_vulnerabilities:
        matched_ids.add(vulnerability.id)
    if vulnerability.aliases:
        matched_ids.update(
            alias for alias in vulnerability.aliases if alias in ignore_vulnerabilities
        )
    return matched_ids


def is_unfixed(vulnerability: Vulnerability) -> bool:
    """Return whether a vulnerability currently has no known fix versions."""

    return vulnerability.fixed_in is None or len(vulnerability.fixed_in) == 0
