"""Vulnerability detection analyzer using OSV.dev"""

from typing import Any

from provchain.data.cache import Cache
from provchain.data.db import Database
from provchain.data.models import (
    AnalysisResult,
    Finding,
    PackageMetadata,
    RiskLevel,
    VulnerabilityResult,
)
from provchain.integrations.osv import OSVClient
from provchain.interrogator.analyzers.base import BaseAnalyzer
from provchain.interrogator.cvss import CVSSCalculator


class VulnerabilityAnalyzer(BaseAnalyzer):
    """Analyzer for detecting known vulnerabilities in packages"""

    name = "vulnerability"

    def __init__(self, cache: Cache | None = None, db: Database | None = None):
        """Initialize vulnerability analyzer

        Args:
            cache: Optional cache for API responses
            db: Optional database for storing results
        """
        self.cache = cache
        self.db = db

    def analyze(self, package_metadata: PackageMetadata) -> AnalysisResult:
        """Analyze package for known vulnerabilities

        Args:
            package_metadata: Package metadata to analyze

        Returns:
            AnalysisResult with vulnerability findings
        """
        package = package_metadata.identifier
        findings = []
        risk_score = 0.0

        try:
            with OSVClient(cache=self.cache) as osv:
                vulnerabilities = osv.get_vulnerabilities_for_package(package)

                if not vulnerabilities:
                    # No vulnerabilities found
                    return AnalysisResult(
                        analyzer=self.name,
                        risk_score=0.0,
                        confidence=0.8,  # High confidence if no vulnerabilities
                        findings=[],
                        raw_data={"vulnerabilities_checked": True, "count": 0},
                    )

                # Process each vulnerability
                for vuln in vulnerabilities:
                    # Calculate CVSS score if not already present
                    if vuln.cvss_score is None:
                        # Try to extract CVSS vector from raw data
                        # For now, estimate severity from vulnerability data
                        if vuln.exploit_available:
                            # Higher risk if exploit available
                            vuln_severity = RiskLevel.HIGH
                            vuln_risk = 8.0
                        elif vuln.patch_available:
                            # Medium risk if patch available
                            vuln_severity = RiskLevel.MEDIUM
                            vuln_risk = 5.0
                        else:
                            # Default to medium
                            vuln_severity = RiskLevel.MEDIUM
                            vuln_risk = 6.0
                    else:
                        vuln_severity = vuln.cvss_score.severity
                        vuln_risk = vuln.cvss_score.base_score

                    # Update vulnerability severity
                    vuln.severity = vuln_severity

                    # Calculate risk score (use highest vulnerability)
                    risk_score = max(risk_score, vuln_risk)

                    # Create finding
                    finding_title = f"{vuln.id}: {vuln.summary}"
                    finding_desc = vuln.details or vuln.summary

                    remediation = "Update to a patched version"
                    if vuln.fixed_versions:
                        remediation = f"Update to one of: {', '.join(vuln.fixed_versions)}"
                    elif vuln.patch_available:
                        remediation = "A patch is available - check package repository for updates"
                    else:
                        remediation = "No patch available - monitor for updates"

                    if vuln.exploit_available:
                        remediation += " [EXPLOIT AVAILABLE - URGENT]"

                    findings.append(
                        Finding(
                            id=vuln.id,
                            title=finding_title,
                            description=finding_desc,
                            severity=vuln_severity,
                            evidence=[
                                f"Published: {vuln.published.isoformat() if vuln.published else 'Unknown'}",
                                f"Database: {vuln.database}",
                                f"Exploit available: {vuln.exploit_available}",
                                f"Patch available: {vuln.patch_available}",
                            ],
                            references=vuln.references[:5],  # Limit to 5 references
                            remediation=remediation,
                        )
                    )

                # Calculate confidence based on number of vulnerabilities found
                confidence = min(0.5 + (len(vulnerabilities) * 0.1), 1.0)

                return AnalysisResult(
                    analyzer=self.name,
                    risk_score=min(risk_score, 10.0),
                    confidence=confidence,
                    findings=findings,
                    raw_data={
                        "vulnerabilities_checked": True,
                        "count": len(vulnerabilities),
                        "vulnerabilities": [v.id for v in vulnerabilities],
                    },
                )

        except Exception as e:
            # Graceful degradation - return low risk if API fails
            return AnalysisResult(
                analyzer=self.name,
                risk_score=0.0,
                confidence=0.3,  # Low confidence if API failed
                findings=[
                    Finding(
                        id="vuln_check_failed",
                        title="Vulnerability check failed",
                        description=f"Unable to check vulnerabilities: {str(e)}",
                        severity=RiskLevel.UNKNOWN,
                        evidence=[f"Error: {str(e)}"],
                        remediation="Retry vulnerability check or check manually",
                    )
                ],
                raw_data={"error": str(e)},
            )

    def get_vulnerability_result(self, package_metadata: PackageMetadata) -> VulnerabilityResult:
        """Get detailed vulnerability result for a package

        Args:
            package_metadata: Package metadata to analyze

        Returns:
            VulnerabilityResult with detailed vulnerability information
        """
        package = package_metadata.identifier

        try:
            with OSVClient(cache=self.cache) as osv:
                vulnerabilities = osv.get_vulnerabilities_for_package(package)

                # Calculate CVSS scores for vulnerabilities that don't have them
                for vuln in vulnerabilities:
                    if vuln.cvss_score is None:
                        # Build a pseudo vuln_data dict from available fields
                        vuln_data: dict[str, Any] = {
                            "references": [{"url": u} for u in vuln.references],
                        }
                        vector = CVSSCalculator.extract_cvss_from_vulnerability(vuln_data)
                        if vector and "CVSS:3" in vector:
                            try:
                                vuln.cvss_score = CVSSCalculator.calculate_cvss_score(vector)
                                vuln.severity = vuln.cvss_score.severity
                            except Exception:
                                pass

                # Count by severity
                critical_count = sum(1 for v in vulnerabilities if v.severity == RiskLevel.CRITICAL)
                high_count = sum(1 for v in vulnerabilities if v.severity == RiskLevel.HIGH)
                medium_count = sum(1 for v in vulnerabilities if v.severity == RiskLevel.MEDIUM)
                low_count = sum(1 for v in vulnerabilities if v.severity == RiskLevel.LOW)

                # Calculate overall risk score
                if vulnerabilities:
                    max_cvss = max(
                        (v.cvss_score.base_score if v.cvss_score else 0.0 for v in vulnerabilities),
                        default=0.0,
                    )
                    risk_score = max_cvss
                else:
                    risk_score = 0.0

                confidence = 0.8 if vulnerabilities else 0.9

                return VulnerabilityResult(
                    package=package,
                    vulnerabilities=vulnerabilities,
                    total_count=len(vulnerabilities),
                    critical_count=critical_count,
                    high_count=high_count,
                    medium_count=medium_count,
                    low_count=low_count,
                    risk_score=risk_score,
                    confidence=confidence,
                    raw_data={"source": "osv.dev"},
                )

        except Exception as e:
            return VulnerabilityResult(
                package=package,
                vulnerabilities=[],
                total_count=0,
                risk_score=0.0,
                confidence=0.3,
                raw_data={"error": str(e)},
            )
