"""파이프라인 단계별 캐시 — content hash 기반 선택적 재실행.

각 파이프라인 단계의 입력 해시를 저장하여, 입력이 변하지 않았으면 재실행을 건너뛸 수 있다.
"""
import hashlib
import json
import logging
from typing import Any, Dict, Optional

from app.core.database import SessionLocal

logger = logging.getLogger(__name__)

# DB에 저장하는 대신 프로젝트 디렉토리에 JSON으로 관리 (심플)
from pathlib import Path
from app.core.config import settings


def _cache_path(project_id: str) -> Path:
    return Path(settings.projects_dir) / project_id / "pipeline_cache.json"


def _load_cache(project_id: str) -> Dict[str, str]:
    p = _cache_path(project_id)
    if p.exists():
        try:
            return json.loads(p.read_text(encoding="utf-8"))
        except Exception:
            return {}
    return {}


def _save_cache(project_id: str, cache: Dict[str, str]) -> None:
    p = _cache_path(project_id)
    p.parent.mkdir(parents=True, exist_ok=True)
    # Atomic write — temp file then replace (POSIX atomic)
    tmp = p.with_suffix(".tmp")
    tmp.write_text(json.dumps(cache, ensure_ascii=False, indent=2), encoding="utf-8")
    tmp.replace(p)


def compute_hash(*inputs: Any) -> str:
    """입력들의 MD5 해시 계산."""
    h = hashlib.md5()
    for inp in inputs:
        if isinstance(inp, str):
            h.update(inp.encode("utf-8"))
        elif isinstance(inp, (dict, list)):
            h.update(json.dumps(inp, sort_keys=True, ensure_ascii=False).encode("utf-8"))
        elif inp is not None:
            h.update(str(inp).encode("utf-8"))
    return h.hexdigest()


def is_step_cached(project_id: str, step_name: str, input_hash: str) -> bool:
    """단계의 입력 해시가 캐시와 일치하면 True (재실행 불필요)."""
    cache = _load_cache(project_id)
    cached = cache.get(step_name)
    if cached == input_hash:
        logger.info("Pipeline cache HIT: %s (hash=%s)", step_name, input_hash[:8])
        return True
    return False


def mark_step_completed(project_id: str, step_name: str, input_hash: str) -> None:
    """단계 완료 시 해시 기록."""
    cache = _load_cache(project_id)
    cache[step_name] = input_hash
    _save_cache(project_id, cache)
    logger.info("Pipeline cache SET: %s (hash=%s)", step_name, input_hash[:8])


def invalidate_step(project_id: str, step_name: str) -> None:
    """특정 단계 캐시 무효화."""
    cache = _load_cache(project_id)
    if step_name in cache:
        del cache[step_name]
        _save_cache(project_id, cache)


def invalidate_all(project_id: str) -> None:
    """프로젝트 전체 캐시 무효화."""
    p = _cache_path(project_id)
    if p.exists():
        p.unlink()
