"""백그라운드 태스크 레지스트리 — 활성 태스크 추적 + 시작 시 stale 복구."""
import logging
import threading
from typing import Dict, Optional

logger = logging.getLogger(__name__)

_active_tasks: Dict[str, threading.Thread] = {}
_lock = threading.Lock()


def register_task(task_key: str, thread: threading.Thread) -> bool:
    """태스크 등록. 이미 실행 중이면 False 반환."""
    with _lock:
        existing = _active_tasks.get(task_key)
        if existing and existing.is_alive():
            return False
        _active_tasks[task_key] = thread
        return True


def unregister_task(task_key: str) -> None:
    with _lock:
        _active_tasks.pop(task_key, None)


def is_task_running(task_key: str) -> bool:
    with _lock:
        t = _active_tasks.get(task_key)
        return t is not None and t.is_alive()


# ── 에피소드 단위 자리 잡기 ────────────────────────────────────────
#
# ★왜 따로 있나 — `run-all` 은 category(analysis/image/all) 별로 **다른**
#  키로 등록한다. 앞에서 모든 category 를 훑어 보긴 하지만, 훑은 뒤 실제로
#  등록하기까지 preflight·설정 읽기·스텝 고르기가 끼어 있어 그 사이가 길다.
#  요청 둘이 그 틈에 나란히 훑으면 **둘 다 통과해 각자 등록된다.**
#  그러면 한 에피소드에서 배치 두 개가 같이 돌고, 정지 표 하나로는 둘 다
#  못 세운다.
#
# ★한 프로세스 안에서만 막는다. 여러 프로세스로 늘리면 이 dict 가 안 공유되니
#  DB 로 옮겨야 한다 — 지금은 한 대·한 프로세스라 이 범위가 맞다.

_episode_claims: Dict[str, str] = {}   # episode_key → 누가 잡았나(설명용)


def claim_episode_run(episode_key: str, holder: str = "") -> bool:
    """에피소드 하나에 배치 하나 — 잡았으면 True, 이미 남이 잡았으면 False.

    검사와 잡기를 **한 자물쇠 안에서** 한다. 나눠 놓으면 그 사이가 곧 틈이다.
    """
    with _lock:
        if episode_key in _episode_claims:
            return False
        _episode_claims[episode_key] = holder
        return True


def release_episode_run(episode_key: str) -> None:
    with _lock:
        _episode_claims.pop(episode_key, None)


def episode_run_holder(episode_key: str) -> Optional[str]:
    with _lock:
        return _episode_claims.get(episode_key)


def recover_stale_progress(db_session) -> int:
    """시작 시 running 상태로 남은 PipelineProgress를 error로 복구."""
    from app.models.project import PipelineProgress
    count = db_session.query(PipelineProgress).filter(
        PipelineProgress.status == "running"
    ).update({"status": "error", "error_message": "Server restarted during operation"})
    db_session.commit()
    return count
