"""Dependency confusion vulnerability checker."""

import logging
from concurrent.futures import ThreadPoolExecutor, as_completed
from typing import List, Dict

from .dependency_parser import Dependency
from ..utils.registry import check_package_exists

logger = logging.getLogger(__name__)


class VulnerableDependency:
    """Represents a vulnerable dependency."""
    
    def __init__(self, dependency: Dependency):
        self.dependency = dependency
        self.is_vulnerable = False
        self.reason = ""
    
    def to_dict(self) -> Dict:
        """Convert to dictionary."""
        return {
            "name": self.dependency.name,
            "version": self.dependency.version,
            "ecosystem": self.dependency.ecosystem,
            "source": self.dependency.source,
            "is_vulnerable": self.is_vulnerable,
            "reason": self.reason,
        }


class ConfusionChecker:
    """Check dependencies for confusion vulnerabilities."""
    
    def __init__(self, dependencies: List[Dependency], max_threads: int = 10):
        self.dependencies = dependencies
        self.max_threads = max_threads
        self.vulnerable: List[VulnerableDependency] = []
    
    def check_all(self) -> List[VulnerableDependency]:
        """Check all dependencies for vulnerabilities."""
        # Deduplicate by (name, ecosystem) — no point hitting the registry twice
        seen: Dict[str, Dependency] = {}
        for dep in self.dependencies:
            key = f"{dep.ecosystem}:{dep.name.lower()}"
            if key not in seen:
                seen[key] = dep

        unique = list(seen.values())
        logger.info(f"Checking {len(unique)} unique packages ({len(self.dependencies)} total) with {self.max_threads} threads")

        # Map results back: key -> VulnerableDependency
        result_map: Dict[str, VulnerableDependency] = {}

        with ThreadPoolExecutor(max_workers=self.max_threads) as executor:
            futures = {
                executor.submit(self._check_dependency, dep): dep
                for dep in unique
            }
            for future in as_completed(futures):
                result = future.result()
                if result:
                    key = f"{result.dependency.ecosystem}:{result.dependency.name.lower()}"
                    result_map[key] = result

        # Collect vulnerabilities, one entry per original dependency
        for dep in self.dependencies:
            key = f"{dep.ecosystem}:{dep.name.lower()}"
            result = result_map.get(key)
            if result and result.is_vulnerable:
                # Create a fresh VulnerableDependency pointing to this specific dep instance
                v = VulnerableDependency(dep)
                v.is_vulnerable = True
                v.reason = result.reason
                self.vulnerable.append(v)

        logger.info(f"Found {len(self.vulnerable)} vulnerable dependencies")
        return self.vulnerable
    
    def _check_dependency(self, dependency: Dependency) -> VulnerableDependency:
        """Check a single dependency."""
        vuln = VulnerableDependency(dependency)
        
        # Skip common public packages
        if self._is_common_package(dependency.name):
            return vuln
        
        # Check if package exists in public registry
        exists = check_package_exists(dependency.name, dependency.ecosystem)
        
        if not exists:
            vuln.is_vulnerable = True
            vuln.reason = f"Package '{dependency.name}' not found in public {dependency.ecosystem} registry"
            logger.warning(f"Vulnerability detected: {vuln.reason}")
        
        return vuln
    
    def _is_common_package(self, name: str) -> bool:
        """Check if package is a well-known public package."""
        # Common packages that should be skipped
        common = {
            "requests", "numpy", "pandas", "flask", "django", "pytest",
            "express", "react", "vue", "lodash", "axios",
            "rails", "sinatra", "rake",
        }
        return name.lower() in common
