"""비디오 썸네일 생성 워커 — ffmpeg subprocess 로 50% 지점 1장 추출.

PKM/Videos/.thumbs/{doc_id}.jpg 에 저장 후 documents.thumbnail_path 업데이트.
quarantine 상태(needs_conversion=true)인 파일은 건너뜀.

queue_consumer 와의 배선(stage 매핑)은 §1 category 분기와 묶여 있어 본 모듈은
유틸 + process() 진입점만 제공. queue_consumer 측 wiring 은 §1 의존 파트에서.
"""

import subprocess
import unicodedata
from datetime import datetime, timezone
from pathlib import Path

from sqlalchemy.ext.asyncio import AsyncSession

from core.config import settings
from core.utils import setup_logger

logger = setup_logger("thumbnail_worker")

THUMBS_DIR_NAME = "PKM/Videos/.thumbs"
FFMPEG_TIMEOUT = 30


def _resolve_path(file_path: str) -> Path | None:
    """NFC(DB) vs NFD(NFS) 한글 경로 차이 흡수. OCR/STT 서비스와 동일 패턴."""
    candidates = [
        file_path,
        unicodedata.normalize("NFD", file_path),
        unicodedata.normalize("NFC", file_path),
    ]
    for c in candidates:
        p = Path(c)
        if p.exists():
            return p
    parent = Path(file_path).parent
    if parent.exists():
        target = unicodedata.normalize("NFC", Path(file_path).name)
        for child in parent.iterdir():
            if unicodedata.normalize("NFC", child.name) == target:
                return child
    return None


def _probe_duration_seconds(path: Path) -> float | None:
    """ffprobe 로 재생 길이 조회. 실패 시 None."""
    try:
        result = subprocess.run(
            [
                "ffprobe", "-v", "error",
                "-show_entries", "format=duration",
                "-of", "default=noprint_wrappers=1:nokey=1",
                str(path),
            ],
            capture_output=True, text=True, timeout=FFMPEG_TIMEOUT,
        )
        if result.returncode != 0:
            return None
        return float(result.stdout.strip())
    except (subprocess.SubprocessError, ValueError):
        return None


def _extract_thumbnail(source: Path, output: Path, seek_seconds: float) -> bool:
    """ffmpeg 로 seek_seconds 지점 1프레임을 jpg 로 추출. 성공 시 True."""
    output.parent.mkdir(parents=True, exist_ok=True)
    try:
        result = subprocess.run(
            [
                "ffmpeg", "-y",
                "-ss", f"{seek_seconds:.2f}",
                "-i", str(source),
                "-vframes", "1",
                "-vf", "scale='min(640,iw)':-1",
                "-q:v", "3",
                str(output),
            ],
            capture_output=True, text=True, timeout=FFMPEG_TIMEOUT,
        )
        if result.returncode != 0:
            logger.error(f"[thumbnail] ffmpeg 실패: {source.name} — {result.stderr[-400:]}")
            return False
        return output.exists() and output.stat().st_size > 0
    except subprocess.SubprocessError as e:
        logger.error(f"[thumbnail] subprocess 오류: {source.name} — {e}")
        return False


async def process(document_id: int, session: AsyncSession) -> None:
    """영상 문서 썸네일 생성 진입점 (queue_consumer 에서 호출 예정).

    needs_conversion=True 는 skip. 파일 위치가 없으면 NFC/NFD resolver 로 보정.
    """
    from models.document import Document

    doc = await session.get(Document, document_id)
    if not doc:
        logger.error(f"[thumbnail] document_id={document_id} 없음")
        return

    if getattr(doc, "needs_conversion", False):
        logger.info(f"[thumbnail] id={document_id} needs_conversion=true → skip")
        return

    if not doc.file_path:
        logger.warning(f"[thumbnail] id={document_id} file_path 없음")
        return

    raw = str(Path(settings.nas_mount_path) / doc.file_path)
    source = _resolve_path(raw)
    if source is None:
        logger.error(f"[thumbnail] 원본 없음: {raw}")
        return

    duration = _probe_duration_seconds(source)
    seek = (duration * 0.5) if duration and duration > 0 else 1.0

    thumbs_dir = Path(settings.nas_mount_path) / THUMBS_DIR_NAME
    output = thumbs_dir / f"{document_id}.jpg"

    ok = _extract_thumbnail(source, output, seek)
    if not ok:
        return

    doc.thumbnail_path = str(output)
    doc.updated_at = datetime.now(timezone.utc)
    await session.commit()
    logger.info(f"[thumbnail] id={document_id} → {output}")
