"""Typed helpers for {{ module_title }} module metadata serialization."""

from __future__ import annotations

from dataclasses import dataclass
from datetime import UTC, datetime
from typing import Any, Mapping, MutableMapping, Sequence


def _coerce_str(value: Any, default: str | None = None) -> str | None:
    if value is None:
        return default
    text = str(value).strip()
    return text or default


def _coerce_bool(value: Any, default: bool) -> bool:
    if isinstance(value, bool):
        return value
    if value is None:
        return default
    lowered = str(value).strip().lower()
    if lowered in {"1", "true", "yes", "on"}:
        return True
    if lowered in {"0", "false", "no", "off"}:
        return False
    return default


def _coerce_sequence(value: Any) -> tuple[str, ...]:
    if value is None:
        return ()
    if isinstance(value, str):
        cleaned = value.strip()
        return (cleaned,) if cleaned else ()
    if isinstance(value, Sequence):
        return tuple(str(item).strip() for item in value if isinstance(item, str) and item.strip())
    return ()


def _coerce_mapping(value: Any) -> Mapping[str, Any]:
    if isinstance(value, Mapping):
        return value
    return {}


@dataclass(frozen=True)
class {{ module_class_name }}BeatEntrySnapshot:
    """Serialized view of a Celery beat schedule entry."""

    name: str
    task: str
    schedule: str
    args: tuple[Any, ...]
    kwargs: Mapping[str, Any]
    options: Mapping[str, Any]

    @classmethod
    def from_mapping(cls, name: str, payload: Mapping[str, Any] | None) -> "{{ module_class_name }}BeatEntrySnapshot":
        data = payload or {}
        return cls(
            name=name,
            task=_coerce_str(data.get("task"), "unknown") or "unknown",
            schedule=_coerce_str(data.get("schedule"), "unknown") or "unknown",
            args=tuple(data.get("args", ()) if isinstance(data.get("args"), Sequence) else ()),
            kwargs=dict(_coerce_mapping(data.get("kwargs"))),
            options=dict(_coerce_mapping(data.get("options"))),
        )

    def as_dict(self) -> MutableMapping[str, Any]:
        return {
            "name": self.name,
            "task": self.task,
            "schedule": self.schedule,
            "args": list(self.args),
            "kwargs": dict(self.kwargs),
            "options": dict(self.options),
        }


@dataclass(frozen=True)
class {{ module_class_name }}HealthSnapshot:
    """Typed representation of Celery health metadata."""

    module: str
    status: str
    checked_at: datetime
    enabled: bool
    broker_url: str | None
    result_backend: str | None
    timezone: str
    task_default_queue: str
    imports: tuple[str, ...]
    include: tuple[str, ...]
    autodiscover: tuple[str, ...]
    namespace: str | None
    beat_schedule: tuple[{{ module_class_name }}BeatEntrySnapshot, ...]
    registered_tasks: tuple[str, ...]
    features: tuple[str, ...]
    detail: str | None = None

    @classmethod
    def from_mapping(
        cls,
        payload: Mapping[str, Any],
        *,
        module_name: str,
        features: Sequence[str],
        detail: str | None = None,
    ) -> "{{ module_class_name }}HealthSnapshot":
        module = _coerce_str(payload.get("module"), module_name) or module_name
        status = _coerce_str(payload.get("status"), "unknown") or "unknown"
        checked_at_raw = payload.get("checked_at")
        if isinstance(checked_at_raw, datetime):
            checked_at = checked_at_raw
        elif checked_at_raw:
            try:
                checked_at = datetime.fromisoformat(str(checked_at_raw))
            except ValueError:
                checked_at = datetime.now(UTC)
        else:
            checked_at = datetime.now(UTC)
        if checked_at.tzinfo is None:
            checked_at = checked_at.replace(tzinfo=UTC)

        beat_payload = payload.get("beat_schedule")
        beat_schedule: tuple[{{ module_class_name }}BeatEntrySnapshot, ...]
        if isinstance(beat_payload, Mapping):
            beat_schedule = tuple(
                {{ module_class_name }}BeatEntrySnapshot.from_mapping(name, entry)
                for name, entry in sorted(beat_payload.items(), key=lambda item: item[0])
            )
        else:
            beat_schedule = ()

        registered_tasks_payload = payload.get("registered_tasks")
        registered_tasks = tuple(
            str(task)
            for task in (registered_tasks_payload or [])
            if isinstance(task, str) and task
        )

        return cls(
            module=module,
            status=status,
            checked_at=checked_at,
            enabled=_coerce_bool(payload.get("enabled"), True),
            broker_url=_coerce_str(payload.get("broker_url")),
            result_backend=_coerce_str(payload.get("result_backend")),
            timezone=_coerce_str(payload.get("timezone"), "UTC") or "UTC",
            task_default_queue=_coerce_str(payload.get("task_default_queue"), "default") or "default",
            imports=_coerce_sequence(payload.get("imports")),
            include=_coerce_sequence(payload.get("include")),
            autodiscover=_coerce_sequence(payload.get("autodiscover")),
            namespace=_coerce_str(payload.get("namespace")),
            beat_schedule=beat_schedule,
            registered_tasks=registered_tasks,
            features=tuple(str(feature) for feature in features if isinstance(feature, str)),
            detail=detail,
        )


def as_dict(snapshot: {{ module_class_name }}HealthSnapshot) -> MutableMapping[str, Any]:
    """Render a snapshot as a JSON-serializable mapping."""

    payload: MutableMapping[str, Any] = {
        "module": snapshot.module,
        "status": snapshot.status,
        "checked_at": snapshot.checked_at.isoformat(),
        "enabled": snapshot.enabled,
        "broker_url": snapshot.broker_url,
        "result_backend": snapshot.result_backend,
        "timezone": snapshot.timezone,
        "task_default_queue": snapshot.task_default_queue,
        "imports": list(snapshot.imports),
        "include": list(snapshot.include),
        "autodiscover": list(snapshot.autodiscover),
        "namespace": snapshot.namespace,
        "beat_schedule": [entry.as_dict() for entry in snapshot.beat_schedule],
        "registered_tasks": list(snapshot.registered_tasks),
        "features": list(snapshot.features),
    }
    if snapshot.detail:
        payload["detail"] = snapshot.detail
    return payload


__all__ = [
    "{{ module_class_name }}BeatEntrySnapshot",
    "{{ module_class_name }}HealthSnapshot",
    "as_dict",
]
