"""v20 — Entity scoping, mentions counter, temporal scoping.

Combined migration for v1.4.0 memory scoping features:
- Entity scoping columns on semantic_claim (#923)
- Mentions counter on semantic_claim (#924)
- Temporal indexes + valid_from backfill (#925)
"""
from __future__ import annotations

import logging

logger = logging.getLogger(__name__)

VERSION = 20

DESCRIPTION = "Entity scoping + mentions counter + temporal scoping (#923, #924, #925)"

# Keep SQL_UP for the runner's attribute check (must be non-empty string).
SQL_UP = "-- applied via apply() for idempotency"

# Columns to add idempotently
_ALTER_COLUMNS = [
    ("entity_id", "ALTER TABLE semantic_claim ADD COLUMN entity_id TEXT"),
    ("entity_type", "ALTER TABLE semantic_claim ADD COLUMN entity_type TEXT DEFAULT 'agent'"),
    ("mentions", "ALTER TABLE semantic_claim ADD COLUMN mentions INTEGER DEFAULT 1"),
    ("last_mentioned_at", "ALTER TABLE semantic_claim ADD COLUMN last_mentioned_at TEXT"),
]

_POST_SQL = """
-- Backfill valid_from from created_at where NULL (#925)
UPDATE semantic_claim SET valid_from = created_at WHERE valid_from IS NULL;

-- Temporal index for contradiction resolution and expiry filtering (#925)
CREATE INDEX IF NOT EXISTS idx_claim_temporal
    ON semantic_claim(subject, predicate, valid_to);

-- Entity scoping index for filtered retrieval (#923)
CREATE INDEX IF NOT EXISTS idx_claim_entity_scope
    ON semantic_claim(entity_id, status);
"""


async def apply(conn) -> None:
    """Apply migration with idempotent column additions."""
    # Get existing columns on semantic_claim
    cursor = await conn.execute("PRAGMA table_info(semantic_claim)")
    rows = await cursor.fetchall()
    existing_cols = {row[1] for row in rows}  # column name is index 1

    for col_name, ddl in _ALTER_COLUMNS:
        if col_name not in existing_cols:
            await conn.execute(ddl)
            logger.info("v20: added column semantic_claim.%s", col_name)
        else:
            logger.info("v20: column semantic_claim.%s already exists, skipping", col_name)

    # Run remaining SQL (indexes, backfill) — all idempotent via IF NOT EXISTS / WHERE
    await conn.executescript(_POST_SQL)
    await conn.commit()
