from typing import NamedTuple

from tree_sitter import Node

from labels.model.file import Location, LocationReadCloser
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.swift.package_builder import new_cocoa_pods_package
from labels.parsers.cataloger.utils import get_enriched_location
from labels.parsers.collection.ruby import find_method_calls, parse_ruby_with_tree_sitter


class PodDeclaration(NamedTuple):
    name: str
    version: str
    line: int


class CollectedArguments(NamedTuple):
    pod_args: list[str]
    git_url: str | None
    tag: str | None
    first_line: int


def parse_podfile(
    _resolver: Resolver | None,
    _environment: Environment | None,
    reader: LocationReadCloser,
) -> tuple[list[Package], list[Relationship]]:
    content = reader.read_closer.read()
    root_node = parse_ruby_with_tree_sitter(content)
    if not root_node:
        return [], []

    pod_declarations = _extract_pod_declarations(root_node)
    if not pod_declarations:
        return [], []

    packages = _build_packages(pod_declarations, reader.location)

    return packages, []


def _extract_pod_declarations(root_node: Node) -> list[PodDeclaration]:
    pod_declarations: list[PodDeclaration] = []

    pod_calls = find_method_calls(root_node, "pod")

    for pod_call in pod_calls:
        name, version, line = _extract_pod_arguments(pod_call)

        if not name or not version:
            continue

        pod_declarations.append(
            PodDeclaration(
                name=name,
                version=version,
                line=line,
            )
        )

    return pod_declarations


def _extract_pod_arguments(node: Node) -> tuple[str | None, str, int]:
    args_node = node.child_by_field_name("arguments")
    if not args_node:
        return None, "", 0

    collected = _collect_argument_data(args_node)

    if collected.git_url and collected.tag:
        return (
            collected.git_url,
            collected.tag,
            collected.first_line,
        )

    if collected.pod_args:
        return (
            collected.pod_args[0],
            " ".join(collected.pod_args[1:]),
            collected.first_line,
        )

    return None, "", 0


def _collect_argument_data(args_node: Node) -> CollectedArguments:
    pod_args: list[str] = []
    git_url: str | None = None
    tag: str | None = None
    first_line: int = 0

    for child in args_node.children:
        if child.type == "string":
            arg_value, line = _extract_string_value(child)
            if arg_value:
                pod_args.append(arg_value)
                if first_line == 0:
                    first_line = line
        elif child.type == "pair":
            key, value = _extract_from_pair(child)
            if key == ":tag":
                tag = value
            if key == ":git":
                git_url = value

    return CollectedArguments(
        pod_args=pod_args,
        git_url=git_url,
        tag=tag,
        first_line=first_line,
    )


def _extract_string_value(string_node: Node) -> tuple[str | None, int]:
    content_node = string_node.child(1)
    if not content_node or content_node.type != "string_content" or not content_node.text:
        return None, 0

    value = content_node.text.decode("utf-8")
    line = string_node.start_point[0] + 1 if string_node.start_point else 0
    return value, line


def _extract_from_pair(pair_node: Node) -> tuple[str | None, str | None]:
    if len(pair_node.children) < 3:
        return None, None

    key_node = pair_node.children[0]
    key_text = key_node.text.decode("utf-8") if key_node.text else ""
    if key_node.type != "simple_symbol":
        return None, None

    value_node = pair_node.children[2]
    if value_node.type != "string":
        return None, None

    string_value, _ = _extract_string_value(value_node)
    if not string_value:
        return None, None

    if key_text in [":tag", ":git"]:
        return key_text, string_value

    return None, None


def _build_packages(pod_declarations: list[PodDeclaration], location: Location) -> list[Package]:
    packages: list[Package] = []

    for pod_decl in pod_declarations:
        enriched_location = get_enriched_location(
            location,
            is_transitive=False,
            line=pod_decl.line,
            package_manager=PackageManager.COCOAPODS,
        )

        package = new_cocoa_pods_package(
            name=pod_decl.name,
            version=pod_decl.version,
            location=enriched_location,
        )

        if package:
            packages.append(package)

    return packages
