#!/usr/bin/env python3
"""
migrate_alerts_postgres.py — Idempotent DDL for alert tables.

Runs safely on existing DBs (ALTER ... ADD COLUMN IF NOT EXISTS).

Tables:
  alerts                  — canonical alert records + state machine
  incident_signature_state — cooldown tracking per incident signature

Usage:
  DATABASE_URL=postgresql://user:pass@host/db python3 ops/scripts/migrate_alerts_postgres.py [--dry-run]
"""
import os
import sys
import textwrap

# ─── alerts table ─────────────────────────────────────────────────────────────
DDL_ALERTS_CREATE = textwrap.dedent("""\
    CREATE TABLE IF NOT EXISTS alerts (
        alert_ref       TEXT         PRIMARY KEY,
        dedupe_key      TEXT         NOT NULL,
        source          TEXT         NOT NULL DEFAULT 'unknown',
        service         TEXT         NOT NULL,
        env             TEXT         NOT NULL DEFAULT 'prod',
        severity        TEXT         NOT NULL DEFAULT 'P2',
        kind            TEXT         NOT NULL DEFAULT 'custom',
        title           TEXT         NOT NULL DEFAULT '',
        summary         TEXT,
        started_at      TIMESTAMPTZ,
        labels          JSONB,
        metrics         JSONB,
        evidence        JSONB,
        links           JSONB,
        created_at      TIMESTAMPTZ  NOT NULL DEFAULT NOW(),
        last_seen_at    TIMESTAMPTZ  NOT NULL DEFAULT NOW(),
        occurrences     INT          NOT NULL DEFAULT 1,
        -- State machine (added in v2)
        status                  TEXT        NOT NULL DEFAULT 'new',
        processing_lock_until   TIMESTAMPTZ,
        processing_owner        TEXT,
        last_error              TEXT,
        acked_at                TIMESTAMPTZ,
        -- Legacy compat
        ack_status              TEXT        DEFAULT 'pending',
        ack_actor               TEXT,
        ack_note                TEXT,
        ack_at                  TIMESTAMPTZ
    );
""")

# Backward-compat additions (safe on existing tables)
DDL_ALERTS_ADD_COLUMNS = textwrap.dedent("""\
    ALTER TABLE alerts ADD COLUMN IF NOT EXISTS status TEXT NOT NULL DEFAULT 'new';
    ALTER TABLE alerts ADD COLUMN IF NOT EXISTS claimed_at TIMESTAMPTZ;
    ALTER TABLE alerts ADD COLUMN IF NOT EXISTS processing_lock_until TIMESTAMPTZ;
    ALTER TABLE alerts ADD COLUMN IF NOT EXISTS processing_owner TEXT;
    ALTER TABLE alerts ADD COLUMN IF NOT EXISTS last_error TEXT;
    ALTER TABLE alerts ADD COLUMN IF NOT EXISTS acked_at TIMESTAMPTZ;
    ALTER TABLE alerts ADD COLUMN IF NOT EXISTS ack_status TEXT DEFAULT 'pending';
    ALTER TABLE alerts ADD COLUMN IF NOT EXISTS ack_actor TEXT;
    ALTER TABLE alerts ADD COLUMN IF NOT EXISTS ack_note TEXT;
    ALTER TABLE alerts ADD COLUMN IF NOT EXISTS ack_at TIMESTAMPTZ;
""")

DDL_ALERTS_INDEXES = textwrap.dedent("""\
    CREATE INDEX IF NOT EXISTS idx_alerts_dedupe_key
        ON alerts(dedupe_key, created_at);
    CREATE INDEX IF NOT EXISTS idx_alerts_service_env
        ON alerts(service, env, created_at);
    CREATE INDEX IF NOT EXISTS idx_alerts_severity
        ON alerts(severity, created_at);
    CREATE INDEX IF NOT EXISTS idx_alerts_status
        ON alerts(status, created_at);
    CREATE INDEX IF NOT EXISTS idx_alerts_processing_lock
        ON alerts(processing_lock_until)
        WHERE processing_lock_until IS NOT NULL;
""")

# ─── incident_signature_state table ──────────────────────────────────────────
DDL_SIG_STATE = textwrap.dedent("""\
    CREATE TABLE IF NOT EXISTS incident_signature_state (
        signature                    TEXT         PRIMARY KEY,
        last_triage_at               TIMESTAMPTZ,
        last_alert_at                TIMESTAMPTZ  NOT NULL DEFAULT NOW(),
        triage_count_24h             INT          NOT NULL DEFAULT 0,
        occurrences_60m              INT          NOT NULL DEFAULT 0,
        occurrences_60m_bucket_start TIMESTAMPTZ,
        updated_at                   TIMESTAMPTZ  NOT NULL DEFAULT NOW()
    );

    -- Add new columns to existing table (idempotent)
    ALTER TABLE incident_signature_state
        ADD COLUMN IF NOT EXISTS occurrences_60m INT NOT NULL DEFAULT 0;
    ALTER TABLE incident_signature_state
        ADD COLUMN IF NOT EXISTS occurrences_60m_bucket_start TIMESTAMPTZ;

    CREATE INDEX IF NOT EXISTS idx_sig_state_updated
        ON incident_signature_state(updated_at);
    CREATE INDEX IF NOT EXISTS idx_sig_state_last_alert
        ON incident_signature_state(last_alert_at);
""")


def run(dsn: str, dry_run: bool = False) -> None:
    try:
        import psycopg2
    except ImportError:
        print("psycopg2 not installed. Run: pip install psycopg2-binary", file=sys.stderr)
        sys.exit(1)

    all_ddl = [
        ("Create alerts table", DDL_ALERTS_CREATE),
        ("Add state machine columns (idempotent)", DDL_ALERTS_ADD_COLUMNS),
        ("Create alerts indexes", DDL_ALERTS_INDEXES),
        ("Create incident_signature_state table", DDL_SIG_STATE),
    ]

    if dry_run:
        print("=== DRY RUN — DDL that would be executed ===\n")
        for label, ddl in all_ddl:
            print(f"-- {label}\n{ddl}")
        return

    conn = psycopg2.connect(dsn)
    try:
        conn.autocommit = False
        with conn.cursor() as cur:
            for label, ddl in all_ddl:
                print(f"  → {label}")
                cur.execute(ddl)
        conn.commit()
        print("✅ All alert migrations completed successfully.")
    except Exception as e:
        conn.rollback()
        print(f"❌ Migration failed: {e}", file=sys.stderr)
        sys.exit(1)
    finally:
        conn.close()


if __name__ == "__main__":
    dsn = os.getenv("DATABASE_URL") or os.getenv("ALERT_DATABASE_URL")
    if not dsn:
        print("ERROR: DATABASE_URL not set", file=sys.stderr)
        sys.exit(1)
    dry = "--dry-run" in sys.argv
    run(dsn, dry_run=dry)
