"""
Migration: add sqlite-vec ANN index for summary embeddings.

Creates a vec0 virtual table (hippo_vec_summaries) and populates it
from existing embeddings in hippo_summaries. Gracefully skips if
sqlite-vec extension is not available.

Issue: #1184
"""

from __future__ import annotations

import logging
import struct
from typing import Optional

import aiosqlite

logger = logging.getLogger(__name__)

MIGRATION_NAME = "add_vec_index"
MIGRATION_VERSION = 3  # bumps from schema v2


async def needs_migration(conn: aiosqlite.Connection) -> bool:
    """Check if the vec table already exists."""
    async with conn.execute(
        "SELECT name FROM sqlite_master WHERE type='table' AND name='hippo_vec_summaries'"
    ) as cur:
        row = await cur.fetchone()
    return row is None


async def _is_vec_available(conn: aiosqlite.Connection) -> bool:
    """Check if sqlite-vec extension is loaded."""
    try:
        async with conn.execute("SELECT vec_version()") as cur:
            row = await cur.fetchone()
            return row is not None
    except Exception:
        return False


async def migrate(
    conn: aiosqlite.Connection,
    embedding_dim: int = 1024,
) -> None:
    """Create vec0 virtual table and populate from existing embeddings."""
    if not await needs_migration(conn):
        logger.info("Migration %s: already applied, skipping", MIGRATION_NAME)
        return

    if not await _is_vec_available(conn):
        logger.warning(
            "Migration %s: sqlite-vec extension not available, skipping. "
            "Install with: pip install sqlite-vec",
            MIGRATION_NAME,
        )
        return

    logger.info(
        "Migration %s: creating vec0 virtual table (dim=%d)",
        MIGRATION_NAME,
        embedding_dim,
    )

    # Create the vec0 virtual table
    await conn.executescript(
        f"""
        CREATE VIRTUAL TABLE IF NOT EXISTS hippo_vec_summaries USING vec0(
            summary_id TEXT PRIMARY KEY,
            owner_id TEXT partition key,
            embedding float[{embedding_dim}] distance_metric=cosine
        );
        """
    )

    # Populate from existing embeddings
    async with conn.execute(
        "SELECT summary_id, owner_id, embedding FROM hippo_summaries WHERE embedding IS NOT NULL"
    ) as cur:
        rows = await cur.fetchall()

    populated = 0
    skipped = 0
    for summary_id, owner_id, embedding_blob in rows:
        if embedding_blob is None:
            continue
        # Validate dimension
        expected_bytes = embedding_dim * 4
        if len(embedding_blob) != expected_bytes:
            logger.debug(
                "Skipping %s: embedding size %d != expected %d",
                summary_id,
                len(embedding_blob),
                expected_bytes,
            )
            skipped += 1
            continue
        try:
            await conn.execute(
                "INSERT INTO hippo_vec_summaries(summary_id, owner_id, embedding) VALUES (?, ?, ?)",
                (summary_id, owner_id, embedding_blob),
            )
            populated += 1
        except Exception as exc:
            logger.debug("Skipping %s: %s", summary_id, exc)
            skipped += 1

    # Update schema version
    await conn.execute(
        """
        INSERT INTO hippo_config (key, value, updated_at)
        VALUES ('schema_version', ?, strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
        ON CONFLICT(key) DO UPDATE SET
            value = excluded.value,
            updated_at = excluded.updated_at
        """,
        (str(MIGRATION_VERSION),),
    )
    await conn.commit()

    logger.info(
        "Migration %s: complete — populated %d vectors, skipped %d",
        MIGRATION_NAME,
        populated,
        skipped,
    )
