"""Manifest validation for Mira archives."""

from __future__ import annotations

from typing import Any

SUPPORTED_MANIFEST_VERSION = 1


def validate_manifest(manifest: dict[str, Any]) -> None:
    """Validate a manifest dictionary.

    Raises:
        RuntimeError: If the manifest is invalid or unsupported.
    """
    if not isinstance(manifest, dict):
        raise RuntimeError("Manifest must be a JSON object")

    errors: list[str] = []

    version = manifest.get("manifest_version")
    if version is None:
        errors.append("manifest_version is required")
    elif not isinstance(version, int):
        errors.append("manifest_version must be an integer")
    elif version > SUPPORTED_MANIFEST_VERSION:
        errors.append(f"Unsupported manifest_version: {version}")

    _require_str(errors, manifest, "name")
    _require_str(errors, manifest, "version")
    _require_str(errors, manifest, "python_version")
    _require_str(errors, manifest, "python_implementation")
    _require_str(errors, manifest, "os")
    _require_str(errors, manifest, "arch")
    _require_str(errors, manifest, "include")

    pointer_size = manifest.get("pointer_size")
    if not isinstance(pointer_size, int):
        errors.append("pointer_size must be an integer")

    entry_point = manifest.get("entry_point")
    if entry_point is not None and not isinstance(entry_point, str):
        errors.append("entry_point must be a string or null")

    dependencies = manifest.get("dependencies")
    if not isinstance(dependencies, dict):
        errors.append("dependencies must be an object")

    files = manifest.get("files")
    if not isinstance(files, dict):
        errors.append("files must be an object")
    else:
        for key in ("dlls", "libs", "project"):
            value = files.get(key)
            if not isinstance(value, list) or not all(
                isinstance(item, str) for item in value
            ):
                errors.append(f"files.{key} must be a list of strings")

    file_hashes = manifest.get("file_hashes")
    if file_hashes is not None and not isinstance(file_hashes, dict):
        errors.append("file_hashes must be an object if present")

    if errors:
        raise RuntimeError("Invalid manifest: " + "; ".join(errors))


def _require_str(errors: list[str], manifest: dict[str, Any], key: str) -> None:
    value = manifest.get(key)
    if not isinstance(value, str) or not value.strip():
        errors.append(f"{key} must be a non-empty string")
