"""Schema-shape tests: the metadata registry can't silently shrink or drift."""
from __future__ import annotations

from sqlalchemy.dialects import postgresql
from sqlalchemy.schema import CreateIndex, CreateTable

from app import models
from app.models.base import Base


def test_all_expected_tables_registered():
    actual = set(Base.metadata.tables.keys())
    assert actual == models.EXPECTED_TABLES, (
        f"missing={sorted(models.EXPECTED_TABLES - actual)} "
        f"extra={sorted(actual - models.EXPECTED_TABLES)}"
    )


def test_full_ddl_compiles_for_postgres():
    """Every table + index compiles; sorted_tables raises on unresolved
    FK cycles (the chat_sessions<->notes cycle is broken via use_alter)."""
    dialect = postgresql.dialect()
    tables = Base.metadata.sorted_tables  # raises if cycle unresolved
    assert len(tables) == len(models.EXPECTED_TABLES)
    for t in tables:
        str(CreateTable(t).compile(dialect=dialect))
        for ix in t.indexes:
            str(CreateIndex(ix).compile(dialect=dialect))


def test_v1_archived_indexes_carried():
    """v1 had is_archived indexes on 9 tables — all must survive the port."""
    v1_archived = {
        "areas", "field_notes", "plantings", "plants", "species",
        "supplies", "varieties", "watering_stations", "years",
    }
    for name in sorted(v1_archived):
        table = Base.metadata.tables[name]
        assert any(
            [c.name for c in ix.columns] == ["is_archived"]
            for ix in table.indexes
        ), f"{name} lost its is_archived index"


def test_carried_warts_stay_text():
    """The knowingly-carried v1 warts must not get 'improved' by accident."""
    from sqlalchemy import Text

    plants = Base.metadata.tables["plants"]
    assert isinstance(plants.c.quantity.type, Text)
    assert isinstance(plants.c.positions.type, Text)
    field_notes = Base.metadata.tables["field_notes"]
    assert isinstance(field_notes.c.photo_paths.type, Text)
    chat_messages = Base.metadata.tables["chat_messages"]
    assert isinstance(chat_messages.c.tool_calls.type, Text)


def test_garden_scoping_deviation_d1():
    """Every entity table carries garden_id; child/M2M tables don't."""
    scoped = {
        "areas", "watering_stations", "supplies", "years", "species",
        "plantings", "plants", "field_notes", "chat_sessions", "artifacts",
        "activity_log", "notes", "assistants", "scheduled_actions",
        "care_rules", "notification_channels", "notification_dispatches",
        "weather_daily", "embeddings", "onboarding_sessions",
        "resolver_feedback", "ai_usage",
        "garden_members",  # composite PK includes garden_id by design
    }
    unscoped = {
        "area_stations", "field_note_targets", "varieties",
        "overview_versions", "chat_messages", "tool_suggestions",
        "note_targets", "note_media", "note_revisions", "reminder_events",
        "pipeline_runs", "users", "gardens",
        "assistant_templates",
    }
    for name in scoped:
        assert "garden_id" in Base.metadata.tables[name].c, f"{name} missing garden_id"
    for name in unscoped:
        assert "garden_id" not in Base.metadata.tables[name].c, f"{name} has unexpected garden_id"
