"""Data-oriented planning for CRUD router construction."""

from __future__ import annotations

from collections.abc import Callable, Collection, Mapping, Sequence
from dataclasses import dataclass, field
from enum import Enum

from sqlmodel import SQLModel

from auen._query import resolve_pk_field
from auen.config import (
    ALL_OPERATIONS,
    AuthConfig,
    FilterConfig,
    HooksConfig,
    NestedWriteConfig,
    Operation,
    PaginationConfig,
    SchemaConfig,
)
from auen.policy import AllowAll, CrudPolicy
from auen.repository import AsyncCrudRepository, AsyncSqlModelRepository
from auen.schemas import derive_schemas
from auen.types import PKType


@dataclass(frozen=True)
class CrudRouterSpec:
    """Router specification for one SQLModel."""

    model: type[SQLModel]
    schemas: SchemaConfig | None = None
    prefix: str = ""
    tags: tuple[str | Enum, ...] = ()
    operations: Collection[Operation] = ALL_OPERATIONS
    auth: AuthConfig | None = None
    policy: CrudPolicy | None = None
    id_field: str = ""
    pagination: PaginationConfig | None = None
    filters: FilterConfig | None = None
    repository_factory: Callable[[type[SQLModel]], AsyncCrudRepository] = (
        AsyncSqlModelRepository
    )
    hooks: HooksConfig | None = None
    nested_writes: NestedWriteConfig | None = None
    nested_policy_registry: Mapping[type[SQLModel], CrudPolicy] = field(
        default_factory=dict
    )
    include_in_schema: set[Operation] | None = None
    operation_id_prefix: str = ""


@dataclass(frozen=True)
class RouterBuildPlan:
    """Fully resolved runtime plan for one router."""

    model: type[SQLModel]
    model_name: str
    schemas: SchemaConfig
    prefix: str
    tags: tuple[str | Enum, ...]
    operations: Collection[Operation]
    auth: AuthConfig | None
    policy: CrudPolicy
    pk_name: str
    pk_type: PKType
    pagination: PaginationConfig
    filters: FilterConfig | None
    repository_factory: Callable[[type[SQLModel]], AsyncCrudRepository]
    hooks: HooksConfig
    nested_writes: NestedWriteConfig | None
    nested_policy_registry: dict[type[SQLModel], CrudPolicy]
    include_in_schema: set[Operation]
    operation_id_prefix: str


def validate_specs(specs: Sequence[CrudRouterSpec]) -> None:
    """Validate top-level spec collection and model types."""
    if not specs:
        msg = "build_crud_routers() requires at least one CrudRouterSpec"
        raise ValueError(msg)
    for spec in specs:
        if not isinstance(spec.model, type) or not issubclass(spec.model, SQLModel):
            msg = "build_crud_routers() expects SQLModel classes in spec.model"
            raise TypeError(msg)


def resolve_router_plan(spec: CrudRouterSpec) -> RouterBuildPlan:
    """Resolve defaults and derived values into a runtime router plan."""
    model = spec.model
    model_name = model.__name__
    pk_name, pk_type = resolve_pk_field(model, spec.id_field or None)

    schemas = spec.schemas or derive_schemas(model)
    prefix = spec.prefix
    if not prefix:
        prefix = f"/{model_name.lower()}s"
    elif not prefix.startswith("/"):
        prefix = f"/{prefix}"

    tags = spec.tags or (model_name,)
    filters = spec.filters
    effective_filters = (
        filters if filters and (filters.fields or filters.sort_fields) else None
    )

    operations = spec.operations
    return RouterBuildPlan(
        model=model,
        model_name=model_name,
        schemas=schemas,
        prefix=prefix,
        tags=tags,
        operations=operations,
        auth=spec.auth,
        policy=spec.policy or AllowAll(),
        pk_name=pk_name,
        pk_type=pk_type,
        pagination=spec.pagination or PaginationConfig(),
        filters=effective_filters,
        repository_factory=spec.repository_factory,
        hooks=spec.hooks or HooksConfig(),
        nested_writes=spec.nested_writes,
        nested_policy_registry=dict(spec.nested_policy_registry),
        include_in_schema=spec.include_in_schema or set(operations),
        operation_id_prefix=spec.operation_id_prefix or model_name.lower(),
    )
