"""Intake token enforcement — gate task_states INSERTs behind /task intake.

Adds:
- intake_tokens table (stores one-time tokens generated by intake_complete)
- intake_token column on task_states
- BEFORE INSERT trigger: rejects INSERTs without valid unconsumed token
- AFTER INSERT trigger: atomically consumes the token on successful INSERT

The trigger is the enforcement layer — it cannot be bypassed by any Python
code path, only by dropping the trigger itself.  This is a procedural
friction gate, not a security boundary: any code with DB access can call
intake_complete to generate a token.  The goal is to prevent accidental
bypass of the /task guided intake process.
"""

from __future__ import annotations

import aiosqlite


async def up(db: aiosqlite.Connection) -> None:
    # intake_tokens table
    await db.execute("""
        CREATE TABLE IF NOT EXISTS intake_tokens (
            token            TEXT PRIMARY KEY,
            created_at       TEXT NOT NULL,
            expires_at       TEXT NOT NULL,
            consumed_at      TEXT,
            task_id          TEXT
        )
    """)

    # Check if task_states exists (it's created by the base schema, not
    # migrations — skip ALTER/triggers on bare DBs like CI test fixtures)
    cursor = await db.execute(
        "SELECT name FROM sqlite_master WHERE type='table' AND name='task_states'"
    )
    if not await cursor.fetchone():
        return

    # Add intake_token column to task_states (idempotent)
    try:
        await db.execute(
            "ALTER TABLE task_states ADD COLUMN intake_token TEXT"
        )
    except Exception as exc:
        msg = str(exc).lower()
        if "duplicate column" not in msg and "already exists" not in msg:
            raise

    # BEFORE INSERT trigger: validate token exists, is unconsumed, not expired
    # datetime() wrapper normalizes ISO/SQLite format differences
    await db.execute("""
        CREATE TRIGGER IF NOT EXISTS enforce_intake_token
        BEFORE INSERT ON task_states
        WHEN NEW.intake_token IS NULL OR NOT EXISTS (
            SELECT 1 FROM intake_tokens
            WHERE token = NEW.intake_token
              AND consumed_at IS NULL
              AND datetime(expires_at) > datetime('now')
        )
        BEGIN
            SELECT RAISE(ABORT, 'Task requires valid intake token. Use /task skill.');
        END
    """)

    # AFTER INSERT trigger: atomically consume the token
    await db.execute("""
        CREATE TRIGGER IF NOT EXISTS consume_intake_token
        AFTER INSERT ON task_states
        WHEN NEW.intake_token IS NOT NULL
        BEGIN
            UPDATE intake_tokens
               SET consumed_at = datetime('now'),
                   task_id = NEW.task_id
             WHERE token = NEW.intake_token;
        END
    """)


async def down(db: aiosqlite.Connection) -> None:
    await db.execute("DROP TRIGGER IF EXISTS consume_intake_token")
    await db.execute("DROP TRIGGER IF EXISTS enforce_intake_token")
    # Leave the column and table — dropping columns requires table rebuild
