import re

from labels.model.file import LocationReadCloser
from labels.model.indexables import IndexedDict, IndexedList, ParsedValue
from labels.model.package import Package
from labels.model.package_manager import PackageManager
from labels.model.relationship import Relationship
from labels.model.release import Environment
from labels.model.resolver import Resolver
from labels.parsers.cataloger.python.package_builder import new_python_package
from labels.parsers.cataloger.utils import get_enriched_location
from labels.parsers.collection.toml import parse_toml_with_tree_sitter


def parse_pyproject_toml(
    _resolver: Resolver | None,
    _environment: Environment | None,
    reader: LocationReadCloser,
) -> tuple[list[Package], list[Relationship]]:
    file_content = parse_toml_with_tree_sitter(reader.read_closer.read())

    packages = _collect_packages(file_content, reader)

    return packages, []


def _collect_packages(
    file_content: IndexedDict[str, ParsedValue],
    reader: LocationReadCloser,
) -> list[Package]:
    return [
        *_parse_poetry_dependencies(file_content, reader),
        *_parse_uv_dependencies(file_content, reader),
    ]


def _parse_poetry_dependencies(
    file_content: IndexedDict[str, ParsedValue],
    reader: LocationReadCloser,
) -> list[Package]:
    packages: list[Package] = []
    tool = file_content.get("tool")
    if not isinstance(tool, IndexedDict):
        return packages

    poetry = tool.get("poetry")
    if not isinstance(poetry, IndexedDict):
        return packages

    deps = poetry.get("dependencies")
    if isinstance(deps, IndexedDict):
        packages.extend(_get_poetry_packages(reader, deps))

    group = poetry.get("group")
    if isinstance(group, IndexedDict):
        dev = group.get("dev")
        if isinstance(dev, IndexedDict):
            dev_deps = dev.get("dependencies")
            if isinstance(dev_deps, IndexedDict):
                packages.extend(_get_poetry_packages(reader, dev_deps, is_dev=True))

    dev_dependencies = poetry.get("dev-dependencies")
    if isinstance(dev_dependencies, IndexedDict):
        packages.extend(_get_poetry_packages(reader, dev_dependencies, is_dev=True))

    return packages


def _get_poetry_packages(
    reader: LocationReadCloser,
    dependencies: IndexedDict[str, ParsedValue],
    *,
    is_dev: bool = False,
) -> list[Package]:
    packages: list[Package] = []

    items = dependencies.items()

    for name, value in items:
        version = _get_version(value)

        new_location = get_enriched_location(
            reader.location,
            line=dependencies.get_key_position(name).start.line,
            is_dev=is_dev,
            is_transitive=False,
            package_manager=PackageManager.PIP,
        )

        package = new_python_package(name=name, version=version, location=new_location)
        if package:
            packages.append(package)

    return packages


def _get_version(value: ParsedValue) -> str | None:
    if isinstance(value, str):
        return value
    if not isinstance(value, IndexedDict):
        return None
    return str(value.get("version", ""))


def _parse_uv_dependencies(
    content: IndexedDict[str, ParsedValue],
    reader: LocationReadCloser,
) -> list[Package]:
    packages: list[Package] = []
    project = content.get("project")
    if not isinstance(project, IndexedDict):
        return packages

    uv_deps = project.get("dependencies")
    if isinstance(uv_deps, IndexedList):
        packages.extend(_get_uv_packages(reader, uv_deps))

    optional_deps = project.get("optional-dependencies")
    if isinstance(optional_deps, IndexedDict):
        uv_dev_deps = optional_deps.get("dev")
        if isinstance(uv_dev_deps, IndexedList):
            packages.extend(_get_uv_packages(reader, uv_dev_deps, is_dev=True))

    dependency_groups = content.get("dependency-groups")
    if isinstance(dependency_groups, IndexedDict):
        dev_group = dependency_groups.get("dev")
        if isinstance(dev_group, IndexedList):
            packages.extend(_get_uv_packages(reader, dev_group, is_dev=True))

    return packages


def _get_uv_packages(
    reader: LocationReadCloser,
    dependencies: IndexedList[ParsedValue],
    *,
    is_dev: bool = False,
) -> list[Package]:
    packages: list[Package] = []
    dep_pattern = re.compile(r"^([A-Za-z0-9_.\-]+)\s*([<>=!~]+.*)?$")

    for index, dep_string in enumerate(dependencies):
        if not isinstance(dep_string, str):
            continue

        match = dep_pattern.match(dep_string.strip())
        if match:
            name = match.group(1)
            version = str(match.group(2)).strip() if match.group(2) else "*"
        else:
            name = dep_string.strip()
            version = "*"

        new_location = get_enriched_location(
            reader.location,
            line=dependencies.get_position(index).start.line,
            is_dev=is_dev,
            is_transitive=False,
            package_manager=PackageManager.PIP,
        )

        package = new_python_package(name=name, version=version, location=new_location)
        if package:
            packages.append(package)

    return packages
