"""Dependency extraction from various ecosystems."""

import json
import logging
import re
import tomli
from pathlib import Path
from typing import Dict, List, Optional

logger = logging.getLogger(__name__)


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


class DependencyParser:
    """Parse dependencies from various file formats."""
    
    def __init__(self, project_path: Path):
        self.project_path = project_path
        self.dependencies: List[Dependency] = []
    
    def parse_all(self) -> List[Dependency]:
        """Parse all dependency files in the project."""
        self._parse_python()
        self._parse_nodejs()
        self._parse_ruby()
        self._parse_go()
        self._parse_rust()
        
        logger.info(f"Found {len(self.dependencies)} total dependencies")
        return self.dependencies
    
    def _parse_python(self) -> None:
        """Parse Python dependencies."""
        # requirements.txt
        for req_file in self.project_path.rglob("*requirements*.txt"):
            self._parse_requirements_txt(req_file)
        
        # pyproject.toml
        for toml_file in self.project_path.rglob("pyproject.toml"):
            self._parse_pyproject_toml(toml_file)
        
        # setup.py
        for setup_file in self.project_path.rglob("setup.py"):
            self._parse_setup_py(setup_file)
        
        # Pipfile
        for pipfile in self.project_path.rglob("Pipfile"):
            self._parse_pipfile(pipfile)
    
    def _parse_requirements_txt(self, file_path: Path) -> None:
        """Parse requirements.txt file."""
        try:
            content = file_path.read_text(encoding="utf-8")
            for line in content.splitlines():
                line = line.strip()
                # Skip blank lines, comments, pip flags (-r, -c, --hash, --index-url, etc.)
                if not line or line.startswith("#") or line.startswith("-"):
                    continue
                # Strip inline comments and hash verification suffixes
                line = line.split(" --hash=")[0].split(" #")[0].strip()
                if not line:
                    continue

                # Must start with a valid package name character
                match = re.match(r"^([a-zA-Z0-9][a-zA-Z0-9._-]*)", line)
                if match:
                    name = match.group(1)
                    version = "*"
                    if "==" in line:
                        version = line.split("==")[1].split()[0].strip()
                    self.dependencies.append(
                        Dependency(name, version, "pypi", str(file_path.relative_to(self.project_path)))
                    )
        except Exception as e:
            logger.warning(f"Error parsing {file_path}: {e}")
    
    def _parse_pyproject_toml(self, file_path: Path) -> None:
        """Parse pyproject.toml file."""
        try:
            content = file_path.read_bytes()
            data = tomli.loads(content.decode("utf-8"))
            
            # Check poetry dependencies
            if "tool" in data and "poetry" in data["tool"]:
                deps = data["tool"]["poetry"].get("dependencies", {})
                for name, version in deps.items():
                    if name != "python":
                        ver = version if isinstance(version, str) else "*"
                        self.dependencies.append(
                            Dependency(name, ver, "pypi", str(file_path.relative_to(self.project_path)))
                        )
            
            # Check PEP 621 dependencies
            if "project" in data:
                deps = data["project"].get("dependencies", [])
                for dep in deps:
                    match = re.match(r"^([a-zA-Z0-9_-]+)", dep)
                    if match:
                        name = match.group(1)
                        version = "*"
                        if "==" in dep:
                            version = dep.split("==")[1].strip()
                        self.dependencies.append(
                            Dependency(name, version, "pypi", str(file_path.relative_to(self.project_path)))
                        )
        except Exception as e:
            logger.warning(f"Error parsing {file_path}: {e}")
    
    def _parse_setup_py(self, file_path: Path) -> None:
        """Parse setup.py file."""
        try:
            content = file_path.read_text(encoding="utf-8")
            # Look for install_requires
            match = re.search(r"install_requires\s*=\s*\[(.*?)\]", content, re.DOTALL)
            if match:
                deps_str = match.group(1)
                for dep in re.findall(r'["\']([^"\']+)["\']', deps_str):
                    match = re.match(r"^([a-zA-Z0-9_-]+)", dep)
                    if match:
                        name = match.group(1)
                        version = "*"
                        if "==" in dep:
                            version = dep.split("==")[1].strip()
                        self.dependencies.append(
                            Dependency(name, version, "pypi", str(file_path.relative_to(self.project_path)))
                        )
        except Exception as e:
            logger.warning(f"Error parsing {file_path}: {e}")
    
    def _parse_pipfile(self, file_path: Path) -> None:
        """Parse Pipfile."""
        try:
            content = file_path.read_text(encoding="utf-8")
            data = tomli.loads(content)
            
            for section in ["packages", "dev-packages"]:
                if section in data:
                    for name, version in data[section].items():
                        ver = version if isinstance(version, str) else "*"
                        self.dependencies.append(
                            Dependency(name, ver, "pypi", str(file_path.relative_to(self.project_path)))
                        )
        except Exception as e:
            logger.warning(f"Error parsing {file_path}: {e}")
    
    def _parse_nodejs(self) -> None:
        """Parse Node.js dependencies."""
        for pkg_file in self.project_path.rglob("package.json"):
            try:
                data = json.loads(pkg_file.read_text(encoding="utf-8"))
                
                for dep_type in ["dependencies", "devDependencies"]:
                    if dep_type in data:
                        for name, version in data[dep_type].items():
                            self.dependencies.append(
                                Dependency(name, version, "npm", str(pkg_file.relative_to(self.project_path)))
                            )
            except Exception as e:
                logger.warning(f"Error parsing {pkg_file}: {e}")
    
    def _parse_ruby(self) -> None:
        """Parse Ruby dependencies."""
        for gemfile in self.project_path.rglob("Gemfile"):
            try:
                content = gemfile.read_text(encoding="utf-8")
                for line in content.splitlines():
                    match = re.match(r"^\s*gem\s+['\"]([^'\"]+)['\"]", line)
                    if match:
                        name = match.group(1)
                        self.dependencies.append(
                            Dependency(name, "*", "rubygems", str(gemfile.relative_to(self.project_path)))
                        )
            except Exception as e:
                logger.warning(f"Error parsing {gemfile}: {e}")
    
    def _parse_go(self) -> None:
        """Parse Go dependencies."""
        for gomod in self.project_path.rglob("go.mod"):
            try:
                content = gomod.read_text(encoding="utf-8")
                for line in content.splitlines():
                    match = re.match(r"^\s*require\s+([^\s]+)\s+([^\s]+)", line)
                    if match:
                        name = match.group(1)
                        version = match.group(2)
                        self.dependencies.append(
                            Dependency(name, version, "go", str(gomod.relative_to(self.project_path)))
                        )
            except Exception as e:
                logger.warning(f"Error parsing {gomod}: {e}")
    
    def _parse_rust(self) -> None:
        """Parse Rust dependencies."""
        for cargo in self.project_path.rglob("Cargo.toml"):
            try:
                content = cargo.read_bytes()
                data = tomli.loads(content.decode("utf-8"))
                
                if "dependencies" in data:
                    for name, version in data["dependencies"].items():
                        ver = version if isinstance(version, str) else "*"
                        self.dependencies.append(
                            Dependency(name, ver, "crates", str(cargo.relative_to(self.project_path)))
                        )
            except Exception as e:
                logger.warning(f"Error parsing {cargo}: {e}")
