#!/usr/bin/env python3
"""
AwoooP Phase 1 Batch 1 回填腳本
================================
對 incidents / knowledge_entries / playbooks / audit_logs 四張表
分批將 project_id IS NULL 的列回填為 'awoooi'。

前置條件：
  awooop_phase1_batch1_rls_2026-05-04.sql Step A（ADD COLUMN nullable）已執行

執行方式：
  從 secret manager / operator vault 設定 DATABASE_URL，禁止在指令或檔案中寫入 URL。
  cd apps/api && python scripts/awooop_phase1_batch1_backfill.py

2026-05-04 ogt + Claude Sonnet 4.6（ADR-118 Batch 1 C-3 修正）
"""

import asyncio
import os
import time

from sqlalchemy import text
from sqlalchemy.ext.asyncio import create_async_engine

DATABASE_URL = os.environ["DATABASE_URL"]

TABLES = [
    ("incidents",         "incident_id"),
    ("knowledge_entries", "id"),
    ("playbooks",         "id"),
    ("audit_logs",        "id"),
]

BATCH_SIZE = 5000
SLEEP_MS = 100  # 批次間休眠 ms，降低對正常流量的影響


async def count_nulls(conn, table: str) -> int:
    result = await conn.execute(
        text(f"SELECT count(*) FROM {table} WHERE project_id IS NULL")  # noqa: S608
    )
    return result.scalar()


async def backfill_table(engine, table: str, pk_col: str) -> int:
    total_updated = 0
    print(f"\n[{table}] 開始回填...")

    while True:
        async with engine.begin() as conn:
            result = await conn.execute(text(f"""
                UPDATE {table}
                   SET project_id = 'awoooi'
                 WHERE {pk_col} IN (
                     SELECT {pk_col} FROM {table}
                      WHERE project_id IS NULL
                      LIMIT :batch_size
                      FOR UPDATE SKIP LOCKED
                 )
            """), {"batch_size": BATCH_SIZE})
            rows = result.rowcount

        total_updated += rows
        if rows == 0:
            break

        print(f"  [{table}] 已回填 {total_updated} 筆...")
        await asyncio.sleep(SLEEP_MS / 1000)

    print(f"  [{table}] 回填完成，共 {total_updated} 筆")
    return total_updated


async def verify(engine) -> bool:
    print("\n=== 驗收確認 ===")
    ok = True
    async with engine.connect() as conn:
        for table, _ in TABLES:
            null_count = await count_nulls(conn, table)
            status = "✅" if null_count == 0 else "❌"
            print(f"  {status} {table}: {null_count} 筆 NULL project_id")
            if null_count != 0:
                ok = False
    return ok


async def main():
    print("=" * 60)
    print("AwoooP Phase 1 Batch 1 Backfill")
    print("=" * 60)

    engine = create_async_engine(DATABASE_URL, echo=False)
    t0 = time.monotonic()

    for table, pk_col in TABLES:
        await backfill_table(engine, table, pk_col)

    passed = await verify(engine)
    elapsed = time.monotonic() - t0

    print(f"\n{'✅ 所有表回填完成' if passed else '❌ 仍有 NULL，請重跑'}")
    print(f"耗時：{elapsed:.1f}s")
    print()
    if passed:
        print("下一步：執行 awooop_phase1_batch1_rls_2026-05-04.sql 的 Step C")
    else:
        print("⚠️  請確認無長 transaction 持有 SKIP LOCKED 的列後重跑")

    await engine.dispose()


if __name__ == "__main__":
    asyncio.run(main())
