#!/usr/bin/python3


import json
import logging
from typing import Any, Optional

import urllib3

from slpkg.config import config_load
from slpkg.utilities import Utilities
from slpkg.views.process import ViewProcess

logger = logging.getLogger(__name__)


class Audit:  # pylint: disable=[R0902,R0903]
    """Checks installed packages for known vulnerabilities (CVEs)."""

    def __init__(self, options: dict[str, Any], packages: Optional[list[str]] = None) -> None:
        self.options = options
        self.packages = packages or []
        self.utils = Utilities()
        self.view_process = ViewProcess()

        self.http = urllib3.PoolManager(timeout=urllib3.Timeout(connect=5.0, read=30.0))
        self.api_url = "https://api.osv.dev/v1/querybatch"

        self.cyan = config_load.cyan
        self.green = config_load.green
        self.red = config_load.red
        self.yellow = config_load.yellow
        self.endc = config_load.endc
        self.bold = config_load.bold

    def _query_osv_batch(self, pkg_list: list[tuple[str, str]]) -> dict[str, list[dict[str, Any]]]:
        """Query the OSV API for multiple packages in one request."""
        queries = []
        for name, version in pkg_list:
            # Clean name (remove SBo, alien tags)
            clean_name = name.split("-")[0]
            queries.append({
                "version": version,
                "package": {"name": clean_name}
            })

        try:
            encoded_data = json.dumps({"queries": queries}).encode("utf-8")
            response = self.http.request(
                "POST",
                self.api_url,
                body=encoded_data,
                headers={"Content-Type": "application/json"}
            )

            if response.status == 200:
                results = json.loads(response.data.decode("utf-8")).get("results", [])
                found_vulns = {}
                for i, result in enumerate(results):
                    vulns = result.get("vulns", [])
                    if vulns:
                        original_name = pkg_list[i][0]
                        found_vulns[original_name] = vulns
                return found_vulns
            return {}
        except (urllib3.exceptions.HTTPError, json.JSONDecodeError) as e:
            logger.error("OSV Batch API query failed: %s", e)
            return {}

    def run(self) -> None:
        """Main execution logic for audit."""
        all_installed = self.utils.all_installed()

        # Determine target packages
        to_audit = self._get_packages_to_audit(all_installed)
        if not to_audit:
            print(f"{self.red}Error{self.endc}: No matching packages found to audit.")
            return

        self.view_process.message(f"Auditing {len(to_audit)} packages for vulnerabilities")

        # Prepare package list for batch query
        pkg_list = []
        for pkg_name, full_name in to_audit.items():
            version = self.utils.split_package(full_name).get("version", "")
            if version:
                pkg_list.append((pkg_name, version))

        # Perform batch query in chunks of 500 (safe limit)
        found_vulns = {}
        chunk_size = 500
        for i in range(0, len(pkg_list), chunk_size):
            chunk = pkg_list[i:i + chunk_size]
            chunk_results = self._query_osv_batch(chunk)
            found_vulns.update(chunk_results)

        self.view_process.done()
        self._display_results(found_vulns, len(to_audit))

    def _get_packages_to_audit(self, all_installed: dict[str, str]) -> dict[str, str]:
        """Filter installed packages based on user request/patterns."""
        if not self.packages:
            return all_installed

        dummy_data: dict[str, dict[str, str]] = {name: {} for name in all_installed}
        matched = self.utils.apply_package_pattern(dummy_data, self.packages)
        return {name: all_installed[name] for name in matched if name in all_installed}

    def _display_results(self, found_vulns: dict[str, list[dict[str, Any]]], total_checked: int) -> None:
        """Display the audit report."""
        if not found_vulns:
            print(f"{self.green}No known vulnerabilities found.{self.endc} (checked {total_checked} packages)")
            return

        print(f"\n{self.red}Found vulnerabilities in {len(found_vulns)} packages:{self.endc}")

        for pkg, vulns in found_vulns.items():
            print(f"\n{self.bold}{pkg}{self.endc}:")
            for v in vulns:
                v_id = v.get("id", "Unknown ID")

                # Robust summary extraction from various OSV fields
                summary = v.get("summary") or v.get("details")

                # If still no summary, try database_specific fields
                if not summary and "database_specific" in v:
                    db_spec = v["database_specific"]
                    if isinstance(db_spec, dict):
                        summary = db_spec.get("summary") or db_spec.get("details")

                if summary:
                    summary = (summary[:100] + "..") if len(summary) > 102 else summary
                    summary = summary.replace("\n", " ").strip()
                else:
                    summary = "No description available in the database."

                print(f"  - {self.yellow}{v_id}{self.endc}: {summary}")
                if "aliases" in v:
                    print(f"    Aliases: {', '.join(v['aliases'])}")

        print(f"\nTotal vulnerable packages: {self.red}{len(found_vulns)}{self.endc}")
        print(f"Visit {self.cyan}https://osv.dev{self.endc} for more details.")
