"""Print Engine — registry for entity-type print template specifications.

Usage (in a module's ``apps.py`` ``ready()`` hook)::

    from simorgh.apps.platform_core.print_registry import (
        print_registry,
        PrintTemplateSpec,
    )

    print_registry.register(PrintTemplateSpec(
        entity_type="crm.invoice",
        template_code="invoice_default",
        display_name="Default Invoice",
        description="Standard A4 invoice template with company header.",
        queryset_fn=my_invoice_queryset,
        context_fn=my_invoice_context,
        paper_size="A4",
        orientation="portrait",
    ))

``queryset_fn`` signature::

    def my_invoice_queryset(*, tenant, actor, object_id) -> Model:
        ...

``context_fn`` signature::

    def my_invoice_context(obj, *, language: str = "en") -> dict[str, Any]:
        ...
"""

from __future__ import annotations

from collections.abc import Callable
from dataclasses import dataclass
from typing import Any

__all__ = [
    "PrintRegistry",
    "PrintTemplateSpec",
    "print_registry",
]


@dataclass
class PrintTemplateSpec:
    """Schema + data-fetcher for one entity type's print output."""

    entity_type: str
    template_code: str
    display_name: str = ""
    description: str = ""
    paper_size: str = "A4"
    orientation: str = "portrait"
    queryset_fn: Callable[..., Any] | None = None
    context_fn: Callable[..., dict[str, Any]] | None = None
    is_default: bool = False
    is_active: bool = True


class PrintRegistry:
    """Singleton registry mapping (entity_type, template_code) → PrintTemplateSpec."""

    def __init__(self) -> None:
        self._specs: dict[tuple[str, str], PrintTemplateSpec] = {}

    def register(self, spec: PrintTemplateSpec) -> None:
        key = (spec.entity_type, spec.template_code)
        if key in self._specs:
            raise ValueError(
                f"PrintTemplateSpec for entity_type={spec.entity_type!r} "
                f"template_code={spec.template_code!r} is already registered."
            )
        self._specs[key] = spec

    def replace(self, spec: PrintTemplateSpec) -> None:
        key = (spec.entity_type, spec.template_code)
        self._specs[key] = spec

    def unregister(self, entity_type: str, template_code: str) -> None:
        self._specs.pop((entity_type, template_code), None)

    def get(self, entity_type: str, template_code: str) -> PrintTemplateSpec | None:
        return self._specs.get((entity_type, template_code))

    def list_for_entity(self, entity_type: str) -> list[PrintTemplateSpec]:
        return [
            s for (et, _), s in self._specs.items()
            if et == entity_type
        ]

    def list_all(self) -> list[PrintTemplateSpec]:
        return list(self._specs.values())

    def all_entity_types(self) -> list[str]:
        return sorted({et for et, _ in self._specs})

    def __contains__(self, entity_type: str) -> bool:
        return any(et == entity_type for et, _ in self._specs)

    def __len__(self) -> int:
        return len(self._specs)


print_registry = PrintRegistry()
