"""Configuration objects for auen CRUD routers."""

from __future__ import annotations

import enum
from collections.abc import Awaitable, Callable, Sequence
from typing import Literal

from pydantic import BaseModel, Field
from pydantic.dataclasses import dataclass
from sqlmodel import SQLModel

from auen.types import User


class Operation(enum.Enum):
    """CRUD operations that can be enabled/disabled."""

    CREATE = "create"
    LIST = "list"
    READ = "read"
    UPDATE = "update"
    NESTED_CREATE = "nested_create"
    NESTED_UPDATE = "nested_update"
    DELETE = "delete"
    BULK_CREATE = "bulk_create"
    BULK_UPDATE = "bulk_update"
    BULK_DELETE = "bulk_delete"


ALL_OPERATIONS: frozenset[Operation] = frozenset(Operation)


class FilterOp(enum.Enum):
    """Supported filter operations."""

    EQ = "eq"
    LT = "lt"
    LTE = "lte"
    GT = "gt"
    GTE = "gte"
    CONTAINS = "contains"
    ICONTAINS = "icontains"
    IN = "in"


@dataclass(frozen=True)
class SchemaConfig:
    """Schemas used for request/response bodies.

    ``create``/``read``/``update`` must be provided.
    Use ``derive_schemas`` to auto-generate nested schemas.
    """

    create: type[BaseModel]
    read: type[BaseModel]
    update: type[BaseModel]
    nested_create: type[BaseModel] | None = None
    nested_update: type[BaseModel] | None = None


@dataclass(frozen=True)
class AuthConfig:
    """Authentication/authorization configuration.

    ``dependency`` is a FastAPI dependency callable that returns
    the current user or raises HTTP 401.
    """

    dependency: Callable[..., User]
    mode: Literal["depends", "security"] = "depends"
    scopes_by_operation: dict[Operation, list[str]] = Field(default_factory=dict)
    allow_anonymous: set[Operation] = Field(default_factory=set)


@dataclass(frozen=True)
class PaginationConfig:
    """Pagination settings for LIST endpoints."""

    default_limit: int = 50
    max_limit: int = 200
    style: Literal["limit_offset", "page"] = "limit_offset"
    include_total: bool = False


@dataclass(frozen=True)
class FilterFieldConfig:
    """Allowed filter operations for a single field."""

    ops: frozenset[FilterOp] = frozenset({FilterOp.EQ})


@dataclass(frozen=True)
class FilterConfig:
    """Filtering and sorting configuration for LIST."""

    fields: dict[str, FilterFieldConfig] = Field(default_factory=dict)
    sort_fields: Sequence[str] = ()


# Event hook type: async callable receiving (session, obj, user)
EventHook = Callable[..., Awaitable[None]]


@dataclass(frozen=True)
class HooksConfig:
    """Lifecycle hooks for CRUD operations.

    Each hook is an async callable invoked at the appropriate point.
    Before-hooks can raise exceptions to abort the operation.
    After-hooks are called after the DB commit.

    All hooks receive ``(session, obj, user)`` where ``obj`` is the
    input data (before) or the DB object (after).
    """

    before_create: EventHook | None = None
    after_create: EventHook | None = None
    before_update: EventHook | None = None
    after_update: EventHook | None = None
    before_delete: EventHook | None = None
    after_delete: EventHook | None = None


@dataclass(frozen=True)
class NestedRelationConfig:
    """Configuration for one writable nested relationship."""

    model: type[SQLModel]
    policy: object | None = None


@dataclass(frozen=True)
class NestedWriteConfig:
    """Opt-in nested write configuration for many-to-one relationships.

    Maps relationship field names to nested write relation configuration.
    """

    fields: dict[str, NestedRelationConfig]
