"""Internal branch coverage for schema/relation helpers."""

from __future__ import annotations

from types import SimpleNamespace

import pytest
from sqlalchemy.orm import RelationshipDirection
from sqlmodel import Field, SQLModel

from auen._relations import RelationInfo, _resolve_link_model, inspect_relations
from auen.schemas import (
    _augment_update_with_optional_pk,
    _build_many_to_many_item_schema,
    _build_nested_create_schema,
    _build_nested_update_schema,
    derive_schemas,
)


class InternalModel(SQLModel, table=True):
    __tablename__ = "internal_model"
    id: int | None = Field(default=None, primary_key=True)
    name: str | None = None


def test_relation_info_is_to_one_property() -> None:
    rel = RelationInfo(
        name="x",
        parent_model=InternalModel,
        target_model=InternalModel,
        direction=RelationshipDirection.MANYTOONE,
        uselist=False,
        secondary=None,
        local_columns=("id",),
        remote_columns=("id",),
        sync_pairs=(("id", "id"),),
        secondary_sync_pairs=(),
        local_remote_pairs=(("id", "id"),),
        link_model=None,
    )
    assert rel.is_to_one is True


def test_resolve_link_model_returns_none_when_no_mapper_matches(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    secondary = object()
    fake_mapper = SimpleNamespace(
        registry=SimpleNamespace(
            mappers=[
                SimpleNamespace(local_table=secondary, class_=object),
                SimpleNamespace(local_table=object(), class_=InternalModel),
            ]
        ),
    )
    monkeypatch.setattr("auen._relations.sa_inspect", lambda _m: fake_mapper)
    assert _resolve_link_model(InternalModel, secondary=secondary) is None


def test_inspect_relations_skips_non_sqlmodel_targets(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    fake_rel = SimpleNamespace(
        mapper=SimpleNamespace(class_=object),
        direction=RelationshipDirection.MANYTOONE,
        uselist=False,
        secondary=None,
        local_columns=(),
        remote_side=(),
        synchronize_pairs=(),
        secondary_synchronize_pairs=(),
        local_remote_pairs=(),
    )
    fake_mapper = SimpleNamespace(
        relationships={"bad": fake_rel},
        registry=SimpleNamespace(mappers=[]),
    )
    monkeypatch.setattr("auen._relations.sa_inspect", lambda _m: fake_mapper)
    assert inspect_relations(InternalModel) == {}


def test_augment_update_with_optional_pk_returns_same_when_pk_present() -> None:
    update_schema = derive_schemas(InternalModel).update
    nested_once = _augment_update_with_optional_pk(InternalModel, update_schema)
    nested_twice = _augment_update_with_optional_pk(InternalModel, nested_once)
    assert nested_once is nested_twice


def test_build_many_to_many_item_schema_without_link_model_uses_dict() -> None:
    rel = RelationInfo(
        name="tags",
        parent_model=InternalModel,
        target_model=InternalModel,
        direction=RelationshipDirection.MANYTOMANY,
        uselist=True,
        secondary=None,
        local_columns=("id",),
        remote_columns=("id",),
        sync_pairs=(("id", "id"),),
        secondary_sync_pairs=(("id", "id"),),
        local_remote_pairs=(("id", "id"),),
        link_model=None,
    )
    item_schema = _build_many_to_many_item_schema(
        model=InternalModel,
        relation_name="tags",
        relation_info=rel,
        related_schema=derive_schemas(InternalModel).update,
        flat_cache={},
    )
    assert "link" in item_schema.model_fields


def test_build_nested_update_schema_returns_cached_schema() -> None:
    base = derive_schemas(InternalModel)
    cached = base.update
    result = _build_nested_update_schema(
        model=InternalModel,
        base_update_schema=base.update,
        auto_discover_relations=False,
        nested_writes=None,
        active_path=(),
        nested_cache={(InternalModel, ()): cached},
        flat_cache={InternalModel: base},
    )
    assert result is cached


def test_build_nested_create_schema_returns_cached_schema() -> None:
    base = derive_schemas(InternalModel)
    cached = base.create
    result = _build_nested_create_schema(
        model=InternalModel,
        base_schema=base.create,
        auto_discover_relations=False,
        nested_writes=None,
        active_path=(),
        nested_cache={(InternalModel, ()): cached},
        flat_cache={InternalModel: base},
    )
    assert result is cached
