"""LipSyncPostProcessor — modality `lipsync_post` (CP-8 real implementation).

Replaces the CP-4 stub. Delegates to
`recoil.execution.providers.sync_so.lipsync_video` for the actual lipsync
job. Class name + zero-arg constructor preserved from CP-4 stub.

Payload schema:
    {
        "shot_id": str (required),
        "video_path": str | Path (required),
        "audio_path": str | Path (required),
        "model": str (required, e.g. "lipsync-2.0"),
        "output_format": Optional[str] (default "mp4"),
        "sync_mode": Optional[str] (default "loop"),
        "fps": Optional[int],
        "output_dir": Optional[Path | str] (default $RECOIL_ROOT/_lipsync_outputs),
        "file_stem": Optional[str] (default f"{shot_id}_lipsync"),
        "max_retries": Optional[int] (default 3),
        "timeout_s": Optional[float] (default 600.0),
        "poll_interval_s": Optional[float] (default 5.0),
        "_transport": Optional[Callable] (test-only injection).
    }
"""

from __future__ import annotations

import logging
from pathlib import Path

from recoil.core.paths import RECOIL_ROOT
from recoil.execution.providers import sync_so as _sync_so
from recoil.pipeline.core.registry import (
    MODALITY_LIPSYNC_POST,
    RunResult,
)
from recoil.pipeline.core.runners._shared import (
    _failure_metadata_lipsync as _failure_metadata,
    make_run_result_id,
)

logger = logging.getLogger(__name__)


class LipSyncPostProcessor:
    """ModalityRunner for `lipsync_post` — sync.so lipsync job.

    Stateless. Zero-arg construction.
    """

    modality: str = MODALITY_LIPSYNC_POST

    def __init__(self) -> None:
        pass

    def run(self, payload: dict) -> RunResult:
        shot_id = payload.get("shot_id")
        video_path = payload.get("video_path")
        audio_path = payload.get("audio_path")
        model = payload.get("model")

        if not shot_id or not video_path or not audio_path or not model:
            return RunResult(
                id=make_run_result_id(shot_id, self.modality),
                modality=self.modality,
                success=False,
                error=(
                    f"LipSyncPostProcessor payload missing required keys: "
                    f"shot_id={shot_id!r}, video_path={video_path!r}, "
                    f"audio_path={audio_path!r}, model={model!r}"
                ),
                metadata=_failure_metadata(),
            )

        output_dir_raw = payload.get("output_dir")
        output_dir = (
            Path(output_dir_raw) if output_dir_raw
            else (RECOIL_ROOT / "_lipsync_outputs")
        )
        file_stem = payload.get("file_stem") or f"{shot_id}_lipsync"
        output_format = payload.get("output_format", "mp4")

        try:
            ls = _sync_so.lipsync_video(
                video_path=Path(video_path),
                audio_path=Path(audio_path),
                model_id=model,
                output_format=output_format,
                output_dir=output_dir,
                file_stem=file_stem,
                sync_mode=payload.get("sync_mode", "loop"),
                fps=payload.get("fps"),
                timeout_s=payload.get("timeout_s", 600.0),
                poll_interval_s=payload.get("poll_interval_s", 5.0),
                max_retries=payload.get("max_retries", 3),
                transport=payload.get("_transport"),
            )
        except _sync_so.LipSyncError as e:
            md = _failure_metadata()
            md["error_class"] = type(e).__name__
            return RunResult(
                id=make_run_result_id(shot_id, self.modality),
                modality=self.modality,
                success=False,
                error=f"{type(e).__name__}: {e}",
                metadata=md,
            )
        except Exception as e:  # noqa: BLE001
            md = _failure_metadata()
            md["error_class"] = type(e).__name__
            return RunResult(
                id=make_run_result_id(shot_id, self.modality),
                modality=self.modality,
                success=False,
                error=f"{type(e).__name__}: {e}",
                metadata=md,
            )

        return RunResult(
            id=make_run_result_id(shot_id, self.modality),
            modality=self.modality,
            output_path=str(ls.output_path),
            output_url=None,
            success=True,
            error=None,
            metadata={
                "final_state": "succeeded",
                "cost_usd": float(ls.cost_usd),
                "model": ls.model,
                "job_id": ls.job_id,
                "duration_s": ls.duration_s,
                "video_path": str(video_path),
                "audio_path": str(audio_path),
                "sync_mode": payload.get("sync_mode", "loop"),
                "fps": payload.get("fps"),
            },
        )


__all__ = ["LipSyncPostProcessor"]
