import json
import logging
from typing import Any

from labels.model.package import Package
from labels.model.relationship import Relationship

LOGGER = logging.getLogger(__name__)


def serialize_packages(packages: list[Package]) -> list[dict[str, Any]]:  # type: ignore[explicit-any]
    raw_pkgs = [pkg.serialize() for pkg in packages]
    return validate_pkgs(raw_pkgs)


def validate_pkgs(raw_sbom_pkgs: list[dict[str, Any]]) -> list[dict[str, Any]]:  # type: ignore[explicit-any]
    pkgs_to_remove: list[int] = []
    for index, raw_pkg in enumerate(raw_sbom_pkgs):
        try:
            json.dumps(raw_pkg)
        except TypeError as err:
            LOGGER.warning(
                "Cannot serialize package at index %d: %s. Error: %s",
                index,
                raw_pkg,
                str(err),
            )
            pkgs_to_remove.append(index)

    for index in sorted(pkgs_to_remove, reverse=True):
        raw_sbom_pkgs.pop(index)
    return raw_sbom_pkgs


def build_relationship_map(
    relationships: list[Relationship],
) -> list[dict[str, str | list[str]]]:
    grouped: dict[tuple[str, str], set[str]] = {}

    for rel in relationships:
        if rel.type.value == "dependency-of":
            # Invert relationship and change type to be semantically correct
            # Input: dep_pkg DEPENDENCY_OF package (dep_pkg is a dependency OF package)
            # Output: package DEPENDS_ON dep_pkg (package depends on dep_pkg)
            key = (rel.to_, "depends-on")
            grouped.setdefault(key, set()).add(rel.from_)
        elif rel.type.value == "described-by":
            # from describes to (lock → non-lock)
            key = (rel.from_, rel.type.value)
            grouped.setdefault(key, set()).add(rel.to_)
    result: list[dict[str, str | list[str]]] = [
        {"from": from_ref, "to": list(deps), "type": rel_type}
        for (from_ref, rel_type), deps in grouped.items()
    ]

    return result


def build_sbom_metadata(
    namespace: str, version: str | None, timestamp: str
) -> dict[str, str | None]:
    return {
        "name": namespace,
        "version": version,
        "timestamp": timestamp,
        "tool": "Fluid-Labels",
        "organization": "Fluid attacks",
    }
