"""Google OSV API client for CVE / vulnerability lookups."""
from __future__ import annotations

from typing import Any

import httpx

OSV_QUERY_URL = "https://api.osv.dev/v1/query"
OSV_BATCH_URL = "https://api.osv.dev/v1/querybatch"
TIMEOUT = httpx.Timeout(20.0, connect=10.0)


async def query_vulnerabilities(name: str, version: str) -> list[dict[str, Any]]:
    """Return OSV vulnerability records for a specific package version."""
    payload: dict[str, Any] = {
        "version": version,
        "package": {"name": name, "ecosystem": "PyPI"},
    }
    async with httpx.AsyncClient(timeout=TIMEOUT) as client:
        try:
            resp = await client.post(OSV_QUERY_URL, json=payload)
            resp.raise_for_status()
            data: dict[str, Any] = resp.json()
            return list(data.get("vulns", []))
        except (httpx.HTTPStatusError, httpx.RequestError):
            return []


async def batch_query(packages: list[tuple[str, str]]) -> list[list[dict[str, Any]]]:
    """
    Query OSV for multiple (name, version) pairs in one round-trip.
    Returns a list of vuln lists in the same order as *packages*.
    """
    queries = [
        {"version": ver, "package": {"name": name, "ecosystem": "PyPI"}}
        for name, ver in packages
    ]
    payload: dict[str, Any] = {"queries": queries}
    async with httpx.AsyncClient(timeout=TIMEOUT) as client:
        try:
            resp = await client.post(OSV_BATCH_URL, json=payload)
            resp.raise_for_status()
            data: dict[str, Any] = resp.json()
            results: list[list[dict[str, Any]]] = []
            for item in data.get("results", []):
                results.append(list(item.get("vulns", [])))
            return results
        except (httpx.HTTPStatusError, httpx.RequestError):
            return [[] for _ in packages]


def cvss_to_severity(vuln: dict[str, Any]) -> str:
    """Map CVSS score from an OSV record to a pyguard severity string."""
    for severity_entry in vuln.get("severity", []):
        score_str: str = severity_entry.get("score", "")
        # CVSS v3 vector or numeric score
        if score_str.startswith("CVSS:3"):
            # Extract base score from vector
            parts = score_str.split("/")
            # CVSS v3 vectors don't include score directly; fall back to database_specific
            pass
    # Try database_specific CVSS
    for db in vuln.get("database_specific", {}).values() if isinstance(vuln.get("database_specific"), dict) else []:
        pass

    # Use affected[*].ecosystem_specific.severity if available
    for affected in vuln.get("affected", []):
        eco_sev = affected.get("ecosystem_specific", {}).get("severity", "")
        if eco_sev:
            eco_sev = eco_sev.upper()
            if eco_sev in {"CRITICAL", "HIGH", "MEDIUM", "LOW"}:
                return eco_sev.lower()

    # Fallback: check the severity array for CVSS_V3 type
    for s in vuln.get("severity", []):
        if s.get("type") in {"CVSS_V3", "CVSS_V2"}:
            score_str = s.get("score", "")
            # Parse "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H" → base score via last component
            # Many OSV records omit the numeric score from the vector string itself.
            # Instead, rely on the database_specific.nvd_published_at or cvss fields.
            break

    # If we can't extract a numeric score, default to "medium"
    return "medium"
