import re
from typing import NamedTuple

from gemfileparser import GemfileParser

from labels.model.file import Location, LocationReadCloser
from labels.model.package import Package
from labels.model.relationship import Relationship
from labels.model.release import Environment
from labels.model.resolver import Resolver
from labels.parsers.cataloger.ruby.package_builder import new_gemspec_package
from labels.parsers.cataloger.ruby.utils import format_ruby_version_constraints
from labels.parsers.cataloger.utils import get_enriched_location


class GemDependency(NamedTuple):
    name: str
    version_constraints: list[str]
    is_development: bool
    line: int


DEPENDENCY_PATTERN = re.compile(
    r"\w+\.add(_runtime|_development)?_dependency\s*\(?\s*"
    r"[\"']([^\"']+)[\"']"
    r"((?:\s*,\s*[\"'][^\"']+[\"'])*)"
    r"\s*\)?"
)

VERSION_CONSTRAINT_PATTERN = re.compile(r"[\"']([^\"']+)[\"']")


def parse_gemspec(
    _resolver: Resolver | None,
    _environment: Environment | None,
    reader: LocationReadCloser,
) -> tuple[list[Package], list[Relationship]]:
    content = reader.read_closer.read()
    dependencies = _extract_dependencies_by_line(content)
    packages = _build_packages_from_dependencies(dependencies, reader.location)

    return packages, []


def _extract_dependencies_by_line(content: str) -> list[GemDependency]:
    dependencies: list[GemDependency] = []

    for line_number, line in enumerate(content.splitlines(), start=1):
        preprocessed = GemfileParser.preprocess(line)
        if not preprocessed:
            continue

        dependency = _parse_dependency_line(preprocessed, line_number)
        if dependency:
            dependencies.append(dependency)

    return dependencies


def _parse_dependency_line(line: str, line_number: int) -> GemDependency | None:
    match = DEPENDENCY_PATTERN.search(line)
    if not match:
        return None

    dep_type = match.group(1) or ""
    is_development = dep_type == "_development"
    name = match.group(2)
    version_part = match.group(3)

    version_constraints = VERSION_CONSTRAINT_PATTERN.findall(version_part)
    if not version_constraints:
        return None

    return GemDependency(name, version_constraints, is_development, line_number)


def _build_packages_from_dependencies(
    dependencies: list[GemDependency], base_location: Location
) -> list[Package]:
    packages: list[Package] = []

    for dep in dependencies:
        version = format_ruby_version_constraints(dep.version_constraints)
        if not version:
            continue

        location = get_enriched_location(
            base_location,
            line=dep.line,
            is_transitive=False,
            is_dev=dep.is_development,
        )

        package = new_gemspec_package(name=dep.name, version=version, location=location)
        if package:
            packages.append(package)

    return packages
