from blends.models import Graph, NId
from blends.query import adj
from blends.utilities.text_nodes import node_to_str

from labels.model.file import 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.graph_parser import (
    maybe_node_nid,
    maybe_node_str,
    node_nid,
    node_str,
    parse_ast_graph,
)
from labels.parsers.cataloger.swift.package_builder import new_swift_package_manager_package
from labels.parsers.cataloger.utils import get_enriched_location


def parse_package_swift(
    _resolver: Resolver | None,
    _environment: Environment | None,
    reader: LocationReadCloser,
) -> tuple[list[Package], list[Relationship]]:
    file_content = reader.read_closer.read().encode("utf-8")
    if not file_content or not (graph := parse_ast_graph(file_content, "swift")):
        return [], []

    dependencies = _parse_dependencies(graph)
    if dependencies is None:
        return [], []

    packages = _collect_packages(dependencies, graph, reader)

    return packages, []


def _parse_dependencies(graph: Graph) -> NId | None:
    dependencies = None

    for node in graph.nodes:
        if (
            maybe_node_str(graph, node, "label_type") == "call_expression"
            and (children := adj(graph, node))
            and len(children) == 2
            and maybe_node_str(graph, children[0], "label_text") == "Package"
        ):
            for child in adj(graph, children[1], depth=2):
                name_id = maybe_node_nid(graph, child, "label_field_name")
                if (
                    maybe_node_str(graph, child, "label_type") == "value_argument"
                    and name_id is not None
                    and (name_value_id := adj(graph, name_id)[0])
                    and maybe_node_str(graph, name_value_id, "label_text") == "dependencies"
                ):
                    dependencies = node_nid(graph, child, "label_field_value")
                    break

    return dependencies


def _collect_packages(
    dep_node: NId,
    graph: Graph,
    reader: LocationReadCloser,
) -> list[Package]:
    packages: list[Package] = []

    for package_node in adj(graph, dep_node, depth=2):
        if _is_valid_package_node(graph, package_node):
            source_url, package_version = _extract_package_details(graph, package_node)

            new_location = get_enriched_location(
                reader.location,
                line=int(node_str(graph, package_node, "label_l")),
                package_manager=PackageManager.SWIFT_PM,
            )

            package = new_swift_package_manager_package(
                source_url=source_url, version=package_version, location=new_location
            )

            if package:
                packages.append(package)

    return packages


def _is_valid_package_node(graph: Graph, package_node: NId) -> bool:
    return bool(
        node_str(graph, package_node, "label_type") == "call_expression"
        and (child_value := adj(graph, package_node)[0])
        and node_to_str(graph, child_value) != ".package(",
    )


def _extract_package_details(
    graph: Graph,
    package_node: NId,
) -> tuple[str | None, str | None]:
    package_version = None
    source_url = None

    for argument_node in adj(graph, package_node, 3):
        name_id = maybe_node_nid(graph, argument_node, "label_field_name")
        if (
            maybe_node_str(graph, argument_node, "label_type") != "value_argument"
            or name_id is None
        ):
            continue

        argument_name = node_to_str(graph, name_id)
        value_id = maybe_node_nid(graph, argument_node, "label_field_value")
        if value_id is None:
            continue

        if argument_name == "url":
            source_url = node_to_str(graph, value_id)
        elif argument_name in {"from", "version"}:
            package_version = node_to_str(graph, value_id).replace('"', "").replace("'", "")

    return source_url, package_version
