import logging
from copy import (
    deepcopy,
)
from typing import (
    cast,
)

from labels.artifact.relationship import (
    Relationship,
    RelationshipType,
)
from labels.file.location_read_closer import (
    LocationReadCloser,
)
from labels.file.resolver import (
    Resolver,
)
from labels.internal.collection.json import (
    parse_json_with_tree_sitter,
)
from labels.internal.collection.types import (
    IndexedDict,
    ParsedValue,
)
from labels.model.core import (
    Package,
)
from labels.pkg.cataloger.generic.parser import (
    Environment,
)
from labels.pkg.cataloger.php.model import (
    PhpComposerInstalledEntry,
    PhpComposerLockEntry,
)
from labels.pkg.cataloger.php.package import (
    new_package_from_composer,
)

LOGGER = logging.getLogger(__name__)
EMPTY_DICT: IndexedDict[str, ParsedValue] = IndexedDict()


def parse_composer_lock(
    _: Resolver | None,
    __: Environment | None,
    reader: LocationReadCloser,
) -> tuple[list[Package], list[Relationship]]:
    package_json: IndexedDict[str, ParsedValue] = cast(
        IndexedDict[str, ParsedValue],
        parse_json_with_tree_sitter(reader.read_closer.read()),
    )
    packages: list[Package] = []
    relationships: list[Relationship] = []
    pkgs: IndexedDict[str, ParsedValue] = cast(
        IndexedDict[str, ParsedValue],
        package_json.get("packages", EMPTY_DICT),
    )
    pkgs_dev: IndexedDict[str, ParsedValue] = cast(
        IndexedDict[str, ParsedValue],
        package_json.get("packages-dev", EMPTY_DICT),
    )
    for is_dev, package in [
        *[(False, x) for x in pkgs],
        *[(True, x) for x in pkgs_dev],
    ]:
        if not isinstance(package, IndexedDict):
            continue
        new_location = deepcopy(reader.location)

        if pkg := new_package_from_composer(package, new_location, is_dev=is_dev):
            packages.append(pkg)

    for parsed_pkg in packages:
        if not isinstance(parsed_pkg.metadata, PhpComposerInstalledEntry | PhpComposerLockEntry):
            continue
        for dep_name in parsed_pkg.metadata.require or []:
            package_dep = next(
                (x for x in packages if x.name == dep_name),
                None,
            )
            if package_dep:
                relationships.append(
                    Relationship(
                        from_=parsed_pkg,
                        to_=package_dep,
                        type=RelationshipType.DEPENDENCY_OF_RELATIONSHIP,
                    ),
                )
    return packages, relationships
