"""Shared helpers for async endpoint registration."""

from typing import TypedDict

from pydantic import BaseModel
from sqlalchemy import inspect as sa_inspect
from sqlalchemy.orm import RelationshipDirection
from sqlmodel import SQLModel

from auen._endpoints import RouterContext
from auen.config import Operation
from auen.exceptions import ConfigurationError
from auen.schemas import derive_schemas


class OpKwargs(TypedDict):
    include_in_schema: bool
    operation_id: str


def _op_kwargs(ctx: RouterContext, op: Operation) -> OpKwargs:
    """Build shared route kwargs for include_in_schema and operation_id."""
    return {
        "include_in_schema": op in ctx.include_in_schema,
        "operation_id": f"{ctx.operation_id_prefix}_{op.value}",
    }


def _resolve_relation_fk_field(
    model: type[SQLModel],
    relation_name: str,
    related_model: type[SQLModel],
) -> str:
    inspected = sa_inspect(model)
    relationships = getattr(inspected, "relationships", None)
    if relationships is None or relation_name not in relationships:
        msg = f"Unknown relationship '{relation_name}' on model {model.__name__}"
        raise ConfigurationError(msg)
    relation = relationships[relation_name]
    if relation.direction != RelationshipDirection.MANYTOONE:
        msg = (
            f"Nested writes support only MANYTOONE relations. "
            f"{model.__name__}.{relation_name} is {relation.direction.name}"
        )
        raise ConfigurationError(msg)
    if relation.mapper.class_ is not related_model:
        msg = (
            f"Nested relation '{relation_name}' model mismatch: "
            f"{relation.mapper.class_.__name__} != {related_model.__name__}"
        )
        raise ConfigurationError(msg)
    if len(relation.local_columns) != 1:
        msg = (
            f"Nested writes require exactly one local FK column for "
            f"{model.__name__}.{relation_name}"
        )
        raise ConfigurationError(msg)
    return str(next(iter(relation.local_columns)).name)


def _build_nested_update_schema(ctx: RouterContext) -> type[BaseModel]:
    if ctx.schemas.nested_update is not None:
        return ctx.schemas.nested_update
    nested_schema = derive_schemas(
        ctx.model,
        nested_writes=ctx.nested_writes,
    ).nested_update
    assert nested_schema is not None
    return nested_schema


def _build_nested_create_schema(ctx: RouterContext) -> type[BaseModel]:
    if ctx.schemas.nested_create is not None:
        return ctx.schemas.nested_create
    nested_schema = derive_schemas(
        ctx.model,
        nested_writes=ctx.nested_writes,
    ).nested_create
    assert nested_schema is not None
    return nested_schema
