"""Tests for soft-delete with 90-day retention (v16 migration).

Covers:
  - Soft delete hides claim from normal queries
  - Soft-deleted claim still retrievable with include_deleted=True
  - Restore works and makes claim visible again
  - Queries (get_claims_by_subject, fts, browse, count) exclude soft-deleted
  - Purge removes claims older than retention period
"""

import asyncio
import pytest
from datetime import datetime, timezone, timedelta

from cortex.storage.sqlite_adapter import SQLiteAdapter


@pytest.fixture
async def adapter():
    """In-memory SQLite adapter with all migrations applied."""
    a = SQLiteAdapter(db_path=":memory:")
    await a.initialize()
    yield a
    await a.close()


@pytest.mark.asyncio
async def test_soft_delete_hides_claim(adapter: SQLiteAdapter):
    """Soft-deleting a claim should hide it from get_claim (default)."""
    claim = await adapter.create_claim(
        subject="Andrew",
        predicate="likes",
        object_value="coffee",
        owner_id="test-owner",
    )
    # Verify it exists
    found = await adapter.get_claim(claim.id, owner_id="test-owner")
    assert found is not None
    assert found.id == claim.id

    # Soft delete
    await adapter.delete_claim(claim.id, owner_id="test-owner", hard=False)

    # Should be hidden from default get_claim
    hidden = await adapter.get_claim(claim.id, owner_id="test-owner")
    assert hidden is None

    # But visible with include_deleted=True
    still_there = await adapter.get_claim(claim.id, owner_id="test-owner", include_deleted=True)
    assert still_there is not None
    assert still_there.is_deleted == 1
    assert still_there.deleted_at is not None


@pytest.mark.asyncio
async def test_restore_claim(adapter: SQLiteAdapter):
    """Restoring a soft-deleted claim should make it visible again."""
    claim = await adapter.create_claim(
        subject="Eva",
        predicate="loves",
        object_value="building",
        owner_id="test-owner",
    )
    await adapter.delete_claim(claim.id, owner_id="test-owner", hard=False)

    # Restore
    restored = await adapter.restore_claim(claim.id, owner_id="test-owner")
    assert restored is not None
    assert restored.is_deleted == 0
    assert restored.deleted_at is None
    assert restored.status == "active"

    # Should now be visible in default get_claim
    found = await adapter.get_claim(claim.id, owner_id="test-owner")
    assert found is not None


@pytest.mark.asyncio
async def test_queries_exclude_soft_deleted(adapter: SQLiteAdapter):
    """get_claims_by_subject, count_claims, browse should exclude soft-deleted."""
    c1 = await adapter.create_claim(
        subject="Andrew",
        predicate="likes",
        object_value="tea",
        owner_id="test-owner",
    )
    c2 = await adapter.create_claim(
        subject="Andrew",
        predicate="likes",
        object_value="coffee",
        owner_id="test-owner",
    )

    # Both visible initially
    by_subject = await adapter.get_claims_by_subject("Andrew", owner_id="test-owner")
    assert len(by_subject) == 2

    count = await adapter.count_claims(owner_id="test-owner")
    assert count == 2

    # Soft-delete one
    await adapter.delete_claim(c1.id, owner_id="test-owner", hard=False)

    # Should only see one now
    by_subject = await adapter.get_claims_by_subject("Andrew", owner_id="test-owner")
    assert len(by_subject) == 1
    assert by_subject[0].id == c2.id

    count = await adapter.count_claims(owner_id="test-owner")
    assert count == 1

    # count with include_deleted should still show 2
    count_all = await adapter.count_claims(owner_id="test-owner", include_deleted=True)
    assert count_all == 2


@pytest.mark.asyncio
async def test_browse_excludes_soft_deleted(adapter: SQLiteAdapter):
    """browse_memories should exclude soft-deleted by default, include with flag."""
    c1 = await adapter.create_claim(
        subject="Test",
        predicate="has",
        object_value="value1",
        owner_id="test-owner",
    )
    c2 = await adapter.create_claim(
        subject="Test",
        predicate="has",
        object_value="value2",
        owner_id="test-owner",
    )

    # Soft-delete one
    await adapter.delete_claim(c1.id, owner_id="test-owner", hard=False)

    # Default browse should show 1
    result = await adapter.browse_memories(owner_id="test-owner")
    assert result["total"] == 1

    # include_deleted should show 2
    result_all = await adapter.browse_memories(owner_id="test-owner", include_deleted=True)
    assert result_all["total"] == 2


@pytest.mark.asyncio
async def test_purge_deleted_claims(adapter: SQLiteAdapter):
    """purge_deleted_claims should remove claims older than retention period."""
    c1 = await adapter.create_claim(
        subject="Old",
        predicate="memory",
        object_value="to purge",
        owner_id="test-owner",
    )
    c2 = await adapter.create_claim(
        subject="Recent",
        predicate="memory",
        object_value="to keep",
        owner_id="test-owner",
    )

    # Soft-delete both
    await adapter.delete_claim(c1.id, owner_id="test-owner", hard=False)
    await adapter.delete_claim(c2.id, owner_id="test-owner", hard=False)

    # Manually backdate c1's deleted_at to > 90 days ago
    conn = await adapter._get_conn()
    old_date = (datetime.now(timezone.utc) - timedelta(days=91)).isoformat()
    await conn.execute(
        "UPDATE semantic_claim SET deleted_at = ? WHERE id = ?",
        (old_date, c1.id),
    )
    await conn.commit()

    # Purge with 90-day retention
    purged = await adapter.purge_deleted_claims(retention_days=90)
    assert purged == 1

    # c1 should be gone entirely
    gone = await adapter.get_claim(c1.id, owner_id="test-owner", include_deleted=True)
    assert gone is None

    # c2 should still be soft-deleted (< 90 days)
    still_there = await adapter.get_claim(c2.id, owner_id="test-owner", include_deleted=True)
    assert still_there is not None
    assert still_there.is_deleted == 1


@pytest.mark.asyncio
async def test_hard_delete_still_works(adapter: SQLiteAdapter):
    """hard=True should permanently remove the row immediately."""
    claim = await adapter.create_claim(
        subject="Temp",
        predicate="data",
        object_value="gone forever",
        owner_id="test-owner",
    )
    await adapter.delete_claim(claim.id, owner_id="test-owner", hard=True)

    # Gone completely
    gone = await adapter.get_claim(claim.id, owner_id="test-owner", include_deleted=True)
    assert gone is None


@pytest.mark.asyncio
async def test_restore_nonexistent_returns_none(adapter: SQLiteAdapter):
    """Restoring a claim that doesn't exist should return None."""
    result = await adapter.restore_claim("nonexistent-id", owner_id="test-owner")
    assert result is None
