"""
KM Backfill Reconciler Job
===========================
C1 修復 2026-04-28 ogt + Claude Sonnet 4.6

職責：
  掃描 km:backfill:dlq（Redis），補救因 backfill 失敗而遺漏的
  Path A KM 條目 related_approval_id 回填。

設計動機：
  _backfill_path_a_approval_safe 失敗時會寫 Redis DLQ（km:backfill:dlq），
  由本 job 每 5 分鐘掃描補救，確保 related_approval_id 最終一致。

Feature Flag：
  ENABLE_KM_BACKFILL_RECONCILER=true（預設 true）→ 啟用本 job
  ENABLE_KM_BACKFILL_RECONCILER=false → 停用（回滾用）

策略：
  1. LRANGE km:backfill:dlq 0 99（每次最多 100 筆）
  2. 逐筆 UPDATE knowledge_entries SET related_approval_id = :aid WHERE related_incident_id = :iid AND related_approval_id IS NULL
  3. 成功 → LREM 從 DLQ 移除
  4. 失敗 → 保留 DLQ，等下一次掃描
  5. Job 失敗只記錄 error，不影響主路徑
"""

from __future__ import annotations

import asyncio
import json

import structlog

from src.core.config import settings

logger = structlog.get_logger(__name__)

KM_BACKFILL_DLQ_KEY = "km:backfill:dlq"
_BATCH_SIZE = 100


async def run_km_backfill_reconciler() -> dict:
    """
    執行一次 backfill reconciler。

    Returns:
        dict with processed / success / failed counts
    """
    if not getattr(settings, "ENABLE_KM_BACKFILL_RECONCILER", True):
        logger.debug("km_backfill_reconciler_disabled")
        return {"processed": 0, "success": 0, "failed": 0}

    try:
        from src.core.redis_client import get_redis
        redis = get_redis()
    except Exception as e:
        logger.error("km_backfill_reconciler_redis_unavailable", error=str(e))
        return {"processed": 0, "success": 0, "failed": 0, "error": str(e)}

    records_raw: list[bytes] = await redis.lrange(KM_BACKFILL_DLQ_KEY, 0, _BATCH_SIZE - 1)
    if not records_raw:
        logger.debug("km_backfill_reconciler_no_pending")
        return {"processed": 0, "success": 0, "failed": 0}

    success_count = 0
    failed_count = 0

    for raw in records_raw:
        try:
            record = json.loads(raw)
            incident_id: str = record["incident_id"]
            approval_id: str = record["approval_id"]
        except (json.JSONDecodeError, KeyError) as e:
            logger.warning(
                "km_backfill_reconciler_malformed_record",
                raw=str(raw)[:200],
                error=str(e),
            )
            # 格式錯誤的 record 直接移除（無法補救）
            await redis.lrem(KM_BACKFILL_DLQ_KEY, 1, raw)
            continue

        try:
            await _do_backfill(incident_id, approval_id)
            # 成功 → 從 DLQ 移除
            await redis.lrem(KM_BACKFILL_DLQ_KEY, 1, raw)
            success_count += 1
            logger.info(
                "km_backfill_reconciler_success",
                incident_id=incident_id,
                approval_id=approval_id,
            )
        except Exception as e:
            failed_count += 1
            logger.warning(
                "km_backfill_reconciler_failed",
                incident_id=incident_id,
                approval_id=approval_id,
                error=str(e),
            )
            # 保留 DLQ record，等下一次掃描

    processed = len(records_raw)
    logger.info(
        "km_backfill_reconciler_done",
        processed=processed,
        success=success_count,
        failed=failed_count,
    )
    return {"processed": processed, "success": success_count, "failed": failed_count}


async def _do_backfill(incident_id: str, approval_id: str) -> None:
    """執行單筆 backfill：UPDATE knowledge_entries SET related_approval_id"""
    from sqlalchemy import text as sa_text

    from src.db.base import get_db_context
    async with get_db_context() as db:
        await db.execute(
            sa_text(
                "UPDATE knowledge_entries "
                "SET related_approval_id = :aid "
                "WHERE related_incident_id = :iid "
                "  AND related_approval_id IS NULL"
            ),
            {"aid": approval_id, "iid": incident_id},
        )
        await db.commit()


_RECONCILE_INTERVAL_SEC = 300  # 每 5 分鐘


async def run_km_backfill_reconciler_loop() -> None:
    """
    永久迴圈：每 5 分鐘執行一次 km:backfill:dlq 補掃。
    由 main.py lifespan 透過 asyncio.create_task() 啟動。

    Feature Flag ENABLE_KM_BACKFILL_RECONCILER=false 時跳過每次執行（不退出迴圈）。
    """
    logger.info(
        "km_backfill_reconciler_loop_started",
        interval_sec=_RECONCILE_INTERVAL_SEC,
    )
    while True:
        try:
            await run_km_backfill_reconciler()
        except Exception as e:
            logger.error("km_backfill_reconciler_loop_error", error=str(e))
        await asyncio.sleep(_RECONCILE_INTERVAL_SEC)
