"""이미지 생성 체크포인트 매니저 — 파일 + DB 이중 추적.

각 참조/씬 이미지 생성 결과를 JSON 파일에 원자적으로 저장하여,
서버 재시작/에러 시 중단된 부분부터 재개할 수 있도록 한다.
"""

import json
import logging
import os
import time
from pathlib import Path
from typing import Any, Dict, List, Optional

logger = logging.getLogger(__name__)


class ImageCheckpointManager:
    """이미지 생성 단계별 체크포인트 관리.

    checkpoint file structure:
    {
        "stage": "reference" | "scene",
        "completed": { item_id: { result_data } },
        "failed": { item_id: error_message },
        "updated_at": "ISO timestamp"
    }
    """

    def __init__(self, checkpoint_dir: Path, stage: str):
        self._dir = checkpoint_dir
        self._stage = stage
        self._path = checkpoint_dir / f"{stage}_checkpoint.json"
        self._data: Dict[str, Any] = {
            "stage": stage,
            "completed": {},
            "failed": {},
            "updated_at": "",
        }
        self._load()

    def _load(self) -> None:
        """기존 체크포인트 로드."""
        if self._path.exists():
            try:
                raw = self._path.read_text(encoding="utf-8")
                data = json.loads(raw)
                if isinstance(data, dict) and data.get("stage") == self._stage:
                    self._data = data
                    logger.info(
                        "Checkpoint loaded: %s — %d completed, %d failed",
                        self._stage,
                        len(self._data.get("completed", {})),
                        len(self._data.get("failed", {})),
                    )
            except Exception as exc:
                logger.warning("Checkpoint load failed (%s), starting fresh: %s", self._path, exc)

    def _save(self) -> None:
        """원자적 파일 저장 (tmp → rename)."""
        self._dir.mkdir(parents=True, exist_ok=True)
        from datetime import datetime, timezone
        self._data["updated_at"] = datetime.now(timezone.utc).isoformat()
        tmp_path = self._path.with_suffix(".tmp")
        try:
            tmp_path.write_text(
                json.dumps(self._data, ensure_ascii=False, indent=1),
                encoding="utf-8",
            )
            os.replace(str(tmp_path), str(self._path))
        except Exception as exc:
            logger.error("Checkpoint save failed: %s", exc)

    def mark_completed(self, item_id: str, result: Dict[str, Any]) -> None:
        """항목 완료 기록. failed에서 제거."""
        self._data.setdefault("completed", {})[item_id] = result
        self._data.get("failed", {}).pop(item_id, None)
        self._save()

    def mark_failed(self, item_id: str, error: str) -> None:
        """항목 실패 기록."""
        self._data.setdefault("failed", {})[item_id] = error
        self._save()

    def get_completed(self, item_id: str) -> Dict[str, Any]:
        """완료 기록 한 건 — 없으면 빈 dict.

        ★쓰기만 있고 **읽는 자리가 없었다** (2026-09-20). 그래서 「이미
         맞으면 안 쓴다」를 판단할 수 없어, 메타를 맞추려면 매 방문마다
         다시 써야 했다. 무변경 방문은 **기록도 남기지 않는 것**이 계약이다.
        """
        row = self._data.get("completed", {}).get(item_id)
        return dict(row) if isinstance(row, dict) else {}

    def is_completed(self, item_id: str) -> bool:
        return item_id in self._data.get("completed", {})

    def get_completed_ids(self) -> set:
        return set(self._data.get("completed", {}).keys())

    def get_failed(self) -> Dict[str, str]:
        return dict(self._data.get("failed", {}))

    def get_failed_ids(self) -> set:
        return set(self._data.get("failed", {}).keys())

    @property
    def completed_count(self) -> int:
        return len(self._data.get("completed", {}))

    @property
    def failed_count(self) -> int:
        return len(self._data.get("failed", {}))

    def clear(self) -> None:
        """체크포인트 초기화."""
        self._data = {
            "stage": self._stage,
            "completed": {},
            "failed": {},
            "updated_at": "",
        }
        if self._path.exists():
            self._path.unlink()

    def summary(self) -> Dict[str, Any]:
        """현재 상태 요약."""
        return {
            "stage": self._stage,
            "completed": self.completed_count,
            "failed": self.failed_count,
            "failed_items": self.get_failed(),
        }
