"""Schema derivation tests."""

from typing import get_args, get_origin

from sqlmodel import Field, Relationship, SQLModel
from tests.conftest import Book

from auen import derive_schema_container, derive_schemas
from auen.config import NestedRelationConfig, NestedWriteConfig


class NoPkModel(SQLModel):
    """Model without table=True — PK detection fallback."""

    name: str


class NestedRelatedModel(SQLModel):
    label: str


class Team(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    heroes: list["Hero"] = Relationship(back_populates="team")


class Hero(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    team_id: int | None = Field(default=None, foreign_key="team.id")
    team: Team | None = Relationship(back_populates="heroes")


class BookTag(SQLModel, table=True):
    book_id: int | None = Field(
        default=None,
        foreign_key="schema_book.id",
        primary_key=True,
    )
    tag_id: int | None = Field(
        default=None,
        foreign_key="schema_tag.id",
        primary_key=True,
    )
    weight: int | None = None


class SchemaBook(SQLModel, table=True):
    __tablename__ = "schema_book"
    id: int | None = Field(default=None, primary_key=True)
    title: str
    tags: list["SchemaTag"] = Relationship(
        back_populates="books",
        link_model=BookTag,
    )


class SchemaTag(SQLModel, table=True):
    __tablename__ = "schema_tag"
    id: int | None = Field(default=None, primary_key=True)
    name: str
    books: list[SchemaBook] = Relationship(back_populates="tags", link_model=BookTag)


def test_derive_schemas_with_include() -> None:
    schemas = derive_schemas(Book, include={"title", "isbn", "id"})
    assert "pages" not in schemas.create.model_fields


def test_derive_schemas_with_exclude() -> None:
    schemas = derive_schemas(Book, exclude={"pages"})
    assert "pages" not in schemas.create.model_fields


def test_derive_schemas_pk_fallback() -> None:
    schemas = derive_schemas(NoPkModel)
    assert "name" in schemas.create.model_fields


def test_derive_schema_container_includes_nested_schemas() -> None:
    container = derive_schema_container(Book)
    assert "title" in container.create.model_fields
    assert "title" in container.read.model_fields
    assert "title" in container.update.model_fields
    assert "title" in container.nested_create.model_fields
    assert "title" in container.nested_update.model_fields


def test_derive_schema_container_auto_discovers_relations_with_cycle_guard() -> None:
    hero_container = derive_schema_container(Hero)
    team_container = derive_schema_container(Team)

    assert "team" in hero_container.nested_create.model_fields
    assert "heroes" in team_container.nested_create.model_fields
    assert "team" in hero_container.nested_update.model_fields
    assert "heroes" in team_container.nested_update.model_fields


def test_derive_schema_container_many_to_many_uses_entity_link_envelope() -> None:
    container = derive_schema_container(SchemaBook)
    nested_create_tags_field = container.nested_create.model_fields["tags"]
    nested_create_tags_annotation = nested_create_tags_field.annotation
    nested_create_list_annotation = next(
        arg for arg in get_args(nested_create_tags_annotation) if get_origin(arg) is list
    )
    nested_create_item_model = get_args(nested_create_list_annotation)[0]
    assert "entity" in nested_create_item_model.model_fields
    assert "link" in nested_create_item_model.model_fields

    tags_field = container.nested_update.model_fields["tags"]
    tags_annotation = tags_field.annotation
    list_annotation = next(
        arg for arg in get_args(tags_annotation) if get_origin(arg) is list
    )
    item_model = get_args(list_annotation)[0]
    assert "entity" in item_model.model_fields
    assert "link" in item_model.model_fields


def test_derive_schemas_with_nested_writes() -> None:
    schemas = derive_schemas(
        Book,
        nested_writes=NestedWriteConfig(
            fields={"author": NestedRelationConfig(model=NestedRelatedModel)}
        ),
    )
    assert "author" in schemas.create.model_fields
    assert "author" in schemas.update.model_fields
    assert "author" in schemas.read.model_fields
    assert schemas.nested_create is not None
    assert "author" in schemas.nested_create.model_fields
    assert schemas.nested_update is not None
    assert "author" in schemas.nested_update.model_fields
