"""PID 80f62523 force resume + category=all dispatch.

본 script 는 backend uvicorn 의 background_job 을 우회하고 main thread 에서
sequential 로 run_steps_batch 를 실행한다 (~1-2시간 block 예상).

- preflight_analysis_start (analysis category)
- select_steps_for_category(category="all", mode="resume")
- run_steps_batch(...)

진행 모니터링은 별도 polling (step_run 테이블 + log).
"""
from __future__ import annotations

import logging
import sys
import time
from datetime import datetime, timezone

# Logging — INFO + 파일 출력
LOG_PATH = "/tmp/dispatch_80f62523.log"
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
    handlers=[
        logging.FileHandler(LOG_PATH, mode="a", encoding="utf-8"),
        logging.StreamHandler(sys.stdout),
    ],
)
logger = logging.getLogger("dispatch_80f62523")

PID = "80f62523-775f-4893-9388-d67b17a4339c"
EID = "9a5e0862-f0fd-4f21-9373-ffc60482ebe4"
CATEGORY = "all"
MODE = "resume"


def main() -> int:
    # SQLAlchemy Base.metadata 에 model 등록만 (FK relation 해소). DDL/DML 미실행.
    # init_db() 는 startup 전용 (raw SQL _migrations + ALTER TABLE).
    from app.core.database import SessionLocal, register_models
    register_models()

    from app.services.analysis_dispatch_service import (
        build_opik_context,
        load_project_llm_config,
        preflight_analysis_start,
        rollback_episode_status,
        run_steps_batch,
        select_steps_for_category,
    )

    started_at = datetime.now(timezone.utc).isoformat()
    logger.info("=" * 60)
    logger.info(
        "DISPATCH START: pid=%s eid=%s category=%s mode=%s at=%s",
        PID, EID, CATEGORY, MODE, started_at,
    )
    logger.info("=" * 60)

    # ── short session 으로 preflight/config/selection/context 처리 후 close ──
    # Codex iter (root cause 진단): wrapper db session 을 긴 batch 동안 들고
    # 있으면 SQLAlchemy autobegin 의 read-only transaction 이 'idle in
    # transaction' 으로 남아 다른 connection 의 ALTER/UPDATE 와 lock chain.
    # run_steps_batch 는 자체 SessionLocal() 을 열고 닫음 (analysis_dispatch_
    # service.py:454/578) — wrapper 의 commit 이 아니라 close 가 정합.
    prior_status = None
    project_config = None
    step_ids = None
    opik_context = None
    try:
        with SessionLocal() as bootstrap_db:
            # analysis preflight (category=all 도 analysis 포함)
            prior_status = preflight_analysis_start(bootstrap_db, PID, EID)
            logger.info("Preflight ok. prior_status=%s", prior_status)

            project_config = load_project_llm_config(bootstrap_db, PID)
            logger.info("Project config loaded. keys=%s", list(project_config.keys()))

            step_ids = select_steps_for_category(
                bootstrap_db, PID, CATEGORY, episode_id=EID, mode=MODE,
            )
            logger.info(
                "Steps selected: count=%d, ids=%s",
                len(step_ids), step_ids,
            )

            opik_context = build_opik_context(bootstrap_db, PID, EID)
            logger.info("Opik context: %s", opik_context)

            # autobegin read-only transaction 정리 — `with` 블록 종료 시 close
            # 가 자동 호출되지만 명시적 rollback 으로 transaction 즉시 해제.
            bootstrap_db.rollback()

        # ── inline 실행 (bootstrap session 닫힌 후) ──
        t0 = time.time()
        run_steps_batch(PID, EID, step_ids, MODE, project_config, opik_context)
        elapsed = time.time() - t0

        logger.info("=" * 60)
        logger.info("DISPATCH COMPLETE: elapsed=%.1fs (%.1f min)", elapsed, elapsed / 60)
        logger.info("=" * 60)
        return 0

    except Exception as exc:
        logger.error("DISPATCH FAILED: %s", exc, exc_info=True)
        if prior_status is not None:
            try:
                with SessionLocal() as rb_db:
                    rollback_episode_status(rb_db, EID, prior_status)
                logger.info("Rolled back episode status to %r", prior_status)
            except Exception as rb_exc:
                logger.error("Rollback failed: %s", rb_exc, exc_info=True)
        return 1


if __name__ == "__main__":
    sys.exit(main())
