from copy import deepcopy
from typing import cast

from packageurl import PackageURL

from labels.model.file import LocationReadCloser
from labels.model.indexables import IndexedDict, IndexedList, ParsedValue
from labels.model.package import Language, Package, PackageType
from labels.model.relationship import Relationship
from labels.model.resolver import Resolver
from labels.parsers.cataloger.generic.parser import Environment
from labels.parsers.collection.yaml import parse_yaml_with_tree_sitter


def package_url(name: str, version: str) -> str:
    return PackageURL(  # type: ignore
        type="github",
        namespace="",
        name=name,
        version=version,
        qualifiers=None,
        subpath="",
    ).to_string()


def extract_dep(step: IndexedDict[str, ParsedValue]) -> tuple[str, int] | None:
    uses = step.get("uses")
    if not isinstance(uses, str):
        return None
    if "name" in step:
        line_number = step.get_key_position("name").start.line
    else:
        line_number = step.get_key_position("uses").start.line
    return uses, line_number


def _get_deps(jobs: IndexedDict[str, ParsedValue]) -> list[tuple[str, int]]:
    deps: list[tuple[str, int]] = []
    for job in jobs.values():
        if isinstance(job, IndexedDict):
            steps = job.get("steps")
            if isinstance(steps, IndexedList):
                deps.extend(
                    filter(
                        None,
                        (extract_dep(step) for step in steps if isinstance(step, IndexedDict)),
                    ),
                )
    return deps


def parse_github_actions_deps(
    _: Resolver | None,
    __: Environment | None,
    reader: LocationReadCloser,
) -> tuple[list[Package], list[Relationship]]:
    packages: list[Package] = []
    parsed_content = cast(
        "IndexedDict[str, ParsedValue]",
        parse_yaml_with_tree_sitter(reader.read_closer.read()),
    )
    if not parsed_content:
        return packages, []
    jobs = parsed_content.get("jobs")
    if not jobs or not isinstance(jobs, IndexedDict):
        return packages, []

    for dep, line_number in _get_deps(jobs):
        dep_info = dep.rsplit("@", 1)
        location = deepcopy(reader.location)
        if location.coordinates:
            location.coordinates.line = line_number
        if len(dep_info) == 2:
            packages.append(
                Package(
                    name=dep_info[0],
                    version=dep_info[1],
                    language=Language.GITHUB_ACTIONS,
                    licenses=[],
                    locations=[location],
                    type=PackageType.GithubActionPkg,
                    metadata=None,
                    p_url=package_url(dep_info[0], dep_info[1]),
                ),
            )
    return packages, []
