"""
tests/test_merge_entity_peer_fk.py — Regression tests for #436.

merge_entities() must UPDATE peer.entity_id before DELETE entity,
otherwise PRAGMA foreign_keys = ON raises IntegrityError.
"""
from __future__ import annotations

import pytest
import pytest_asyncio
from datetime import datetime, timezone

from cortex.storage.sqlite_adapter import SQLiteAdapter


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

async def _make_adapter(tmp_path) -> SQLiteAdapter:
    """Create a fully-initialized SQLiteAdapter on a temp DB."""
    db_path = str(tmp_path / "test.db")
    adapter = SQLiteAdapter(db_path=db_path, enable_wal=False)
    await adapter.initialize()
    return adapter


def _now() -> str:
    return datetime.now(timezone.utc).isoformat(timespec="milliseconds")


async def _insert_entity(adapter, entity_id: str, name: str = "test", owner_id: str = "default"):
    """Insert a minimal entity row directly."""
    conn = await adapter._get_conn()
    now = _now()
    await conn.execute(
        """INSERT INTO entity (id, owner_id, entity_type, canonical_name, mention_count, first_seen, last_seen, created_at, updated_at)
           VALUES (?, ?, 'person', ?, 1, ?, ?, ?, ?)""",
        (entity_id, owner_id, name, now, now, now, now),
    )
    await conn.commit()


async def _insert_peer(adapter, peer_id: str, entity_id: str, owner_id: str = "default"):
    """Insert a minimal peer row directly."""
    conn = await adapter._get_conn()
    now = _now()
    await conn.execute(
        """INSERT INTO peer (id, owner_id, entity_id, display_name, first_interaction, last_interaction, created_at, updated_at)
           VALUES (?, ?, ?, 'TestPeer', ?, ?, ?, ?)""",
        (peer_id, owner_id, entity_id, now, now, now, now),
    )
    await conn.commit()


async def _get_peer_entity_id(adapter, peer_id: str) -> str | None:
    """Read entity_id for a peer row."""
    conn = await adapter._get_conn()
    async with conn.execute("SELECT entity_id FROM peer WHERE id = ?", (peer_id,)) as cur:
        row = await cur.fetchone()
    return row[0] if row else None


# ---------------------------------------------------------------------------
# Tests
# ---------------------------------------------------------------------------

@pytest.mark.asyncio
async def test_merge_updates_peer_entity_id(tmp_path):
    """#436: merge_entities must re-point peer.entity_id from merge_id to keep_id."""
    adapter = await _make_adapter(tmp_path)

    await _insert_entity(adapter, "ent-keep", "Alice")
    await _insert_entity(adapter, "ent-merge", "Alise")
    await _insert_peer(adapter, "peer-1", "ent-merge")

    # This should NOT raise IntegrityError
    await adapter.merge_entities("ent-keep", "ent-merge")

    eid = await _get_peer_entity_id(adapter, "peer-1")
    assert eid == "ent-keep", f"Peer entity_id should be updated to keep_id, got {eid}"


@pytest.mark.asyncio
async def test_merge_without_peers_still_works(tmp_path):
    """Regression: merge with no peer rows should still succeed."""
    adapter = await _make_adapter(tmp_path)

    await _insert_entity(adapter, "ent-keep", "Alice")
    await _insert_entity(adapter, "ent-merge", "Alise")

    result = await adapter.merge_entities("ent-keep", "ent-merge")
    assert result is not None
    assert result.id == "ent-keep"


@pytest.mark.asyncio
async def test_merge_with_fk_enforcement_no_integrity_error(tmp_path):
    """With PRAGMA foreign_keys = ON, merge must not raise IntegrityError."""
    adapter = await _make_adapter(tmp_path)

    # Confirm FK enforcement is on
    conn = await adapter._get_conn()
    async with conn.execute("PRAGMA foreign_keys") as cur:
        fk_row = await cur.fetchone()
    assert fk_row[0] == 1, "foreign_keys should be ON"

    await _insert_entity(adapter, "ent-keep", "Alice")
    await _insert_entity(adapter, "ent-merge", "Alise")
    await _insert_peer(adapter, "peer-1", "ent-merge")
    await _insert_peer(adapter, "peer-2", "ent-merge")

    # Should not raise sqlite3.IntegrityError
    result = await adapter.merge_entities("ent-keep", "ent-merge")
    assert result.id == "ent-keep"

    # Both peers should point to keep_id now
    for pid in ("peer-1", "peer-2"):
        eid = await _get_peer_entity_id(adapter, pid)
        assert eid == "ent-keep"


@pytest.mark.asyncio
async def test_merge_both_have_peers(tmp_path):
    """When both keep and merge entities have peers, all should end up on keep_id."""
    adapter = await _make_adapter(tmp_path)

    await _insert_entity(adapter, "ent-keep", "Alice")
    await _insert_entity(adapter, "ent-merge", "Alise")
    await _insert_peer(adapter, "peer-keep", "ent-keep")
    await _insert_peer(adapter, "peer-merge", "ent-merge")

    await adapter.merge_entities("ent-keep", "ent-merge")

    assert await _get_peer_entity_id(adapter, "peer-keep") == "ent-keep"
    assert await _get_peer_entity_id(adapter, "peer-merge") == "ent-keep"
