# #region ManifestBuilder [C:5] [TYPE Module] [SEMANTICS clean-release, manifest, build, artifact, catalog]
# @BRIEF Build deterministic distribution manifest from classified artifact input.
# @LAYER Domain
# @RELATION DEPENDS_ON -> [CleanReleaseModels]
# @INVARIANT  Equal semantic artifact sets produce identical deterministic hash values.
# @SIDE_EFFECT  Computes hash of artifact set
# @DATA_CONTRACT  ArtifactSet -> Manifest

from __future__ import annotations

from collections.abc import Iterable
from datetime import UTC, datetime
import hashlib
import json
from typing import Any

from ...models.clean_release import (
    ClassificationType,
    DistributionManifest,
    ManifestItem,
    ManifestSummary,
)


def _stable_hash_payload(
    candidate_id: str, policy_id: str, items: list[ManifestItem]
) -> str:
    serialized = [
        {
            "path": item.path,
            "category": item.category,
            "classification": item.classification.value,
            "reason": item.reason,
            "checksum": item.checksum,
        }
        for item in sorted(
            items,
            key=lambda i: (
                i.path,
                i.category,
                i.classification.value,
                i.reason,
                i.checksum or "",
            ),
        )
    ]
    payload = {
        "candidate_id": candidate_id,
        "policy_id": policy_id,
        "items": serialized,
    }
    digest = hashlib.sha256(
        json.dumps(payload, ensure_ascii=False, sort_keys=True).encode("utf-8")
    ).hexdigest()
    return digest


# #region build_distribution_manifest [TYPE Function]
# @BRIEF Build DistributionManifest with deterministic hash and validated counters.
# @PRE  artifacts list contains normalized classification values.
# @POST  Returns DistributionManifest with summary counts matching items cardinality.
def build_distribution_manifest(
    manifest_id: str,
    candidate_id: str,
    policy_id: str,
    generated_by: str,
    artifacts: Iterable[dict[str, Any]],
) -> DistributionManifest:
    items = [
        ManifestItem(
            path=a["path"],
            category=a["category"],
            classification=ClassificationType(a["classification"]),
            reason=a["reason"],
            checksum=a.get("checksum"),
        )
        for a in artifacts
    ]

    included_count = sum(
        1
        for item in items
        if item.classification
        in {ClassificationType.REQUIRED_SYSTEM, ClassificationType.ALLOWED}
    )
    excluded_count = sum(
        1
        for item in items
        if item.classification == ClassificationType.EXCLUDED_PROHIBITED
    )
    prohibited_detected_count = excluded_count

    summary = ManifestSummary(
        included_count=included_count,
        excluded_count=excluded_count,
        prohibited_detected_count=prohibited_detected_count,
    )

    deterministic_hash = _stable_hash_payload(candidate_id, policy_id, items)

    return DistributionManifest(
        manifest_id=manifest_id,
        candidate_id=candidate_id,
        generated_at=datetime.now(UTC),
        generated_by=generated_by,
        items=items,
        summary=summary,
        deterministic_hash=deterministic_hash,
    )


# #endregion build_distribution_manifest


# #region build_manifest [TYPE Function]
# @BRIEF Legacy compatibility wrapper for old manifest builder import paths.
# @PRE  Same as build_distribution_manifest.
# @POST  Returns DistributionManifest produced by canonical builder.
def build_manifest(
    manifest_id: str,
    candidate_id: str,
    policy_id: str,
    generated_by: str,
    artifacts: Iterable[dict[str, Any]],
) -> DistributionManifest:
    return build_distribution_manifest(
        manifest_id=manifest_id,
        candidate_id=candidate_id,
        policy_id=policy_id,
        generated_by=generated_by,
        artifacts=artifacts,
    )


# #endregion build_manifest
# #endregion ManifestBuilder
