"""视频合成 Celery 任务 — Phase 8 任务 2.10.

使用 JobService 管理任务生命周期，集成 VideoComposeService 执行合成。
"""

from __future__ import annotations

import logging
import os
import shutil
import subprocess
import tempfile
from datetime import datetime, timezone
from pathlib import Path

from celery.utils.log import get_task_logger
from worker_app.celery_app import celery_app
from worker_app.db import SessionLocal

logger = get_task_logger(__name__)


def _get_job_service():
    """延迟导入 JobService，避免循环依赖。"""
    from apps.api.app.services.job_service import JobService
    from packages.adapters.sqlalchemy_impl.job_repository import SQLAlchemyJobRepository

    db = SessionLocal()
    repo = SQLAlchemyJobRepository(db)
    return JobService(repo), db


@celery_app.task(
    name="worker.compose_video",
    bind=True,
    max_retries=3,
    default_retry_delay=60,
)
def compose_video(self, job_id: str, **kwargs):
    """视频合成任务。

    Args:
        job_id: JobService 中的任务 ID
        **kwargs: 来自 Job.payload 的额外参数（plan_id, output_path 等）
    """
    job_service, db = _get_job_service()

    try:
        job = job_service.get_job(job_id)
        if job is None:
            logger.error("Job not found: %s", job_id)
            return {"status": "error", "message": f"Job {job_id} not found"}

        plan_id = job.payload.get("plan_id", "")
        if not plan_id:
            job_service.fail_job(job_id, "Missing plan_id in job payload")
            return {"status": "error", "message": "Missing plan_id"}

        # 标记为 running
        job_service.update_progress(job_id, progress=10.0, current_stage="初始化合成环境")

        # 延迟导入 VideoComposeService
        from apps.api.app.services.video_compose_service import VideoComposeService

        compose_svc = VideoComposeService(db)

        # 校验合成条件
        job_service.update_progress(job_id, progress=20.0, current_stage="校验合成条件")
        validation = compose_svc.validate_compose(plan_id)
        if not validation.valid:
            error_msg = "; ".join(validation.errors)
            job_service.fail_job(job_id, f"合成校验失败: {error_msg}")
            return {"status": "error", "message": error_msg}

        # 构建合成命令
        job_service.update_progress(job_id, progress=30.0, current_stage="构建 FFmpeg 命令")
        _output_dir = os.environ.get("VIDEO_OUTPUT_DIR", os.path.join(tempfile.gettempdir(), "video_output"))
        output_path = os.path.join(_output_dir, f"{job_id}.mp4")
        compose_cmd = compose_svc.build_compose_command(plan_id, output_path)

        # 执行 FFmpeg
        job_service.update_progress(job_id, progress=50.0, current_stage="正在执行视频合成")
        logger.info("Executing FFmpeg for job %s, plan %s", job_id, plan_id)

        try:
            subprocess.run(
                compose_cmd.command,
                check=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,
                timeout=3600,
            )
        except subprocess.CalledProcessError as e:
            job_service.fail_job(job_id, f"FFmpeg 执行失败: {e.stderr[:500]}")
            raise

        # 上传结果
        job_service.update_progress(job_id, progress=80.0, current_stage="上传合成结果")
        storage_key = f"rendered/{plan_id}/{job_id}.mp4"

        from worker_app.tasks.edit_plan_generation import _upload_to_oss

        output_url = _upload_to_oss(Path(output_path), storage_key)

        # 更新 Job 状态为完成
        result_data = {
            "plan_id": plan_id,
            "output_path": output_path,
            "storage_key": storage_key,
            "output_url": output_url or "",
            "estimated_duration": compose_cmd.estimated_duration,
            "clip_count": len(compose_cmd.clip_chains),
        }
        job_service.complete_job(job_id, result=result_data)

        logger.info("视频合成完成: job_id=%s, plan_id=%s", job_id, plan_id)
        return {"status": "completed", "job_id": job_id, "result": result_data}

    except self.retry_exc as exc:
        logger.warning("视频合成重试中: job_id=%s, exc=%s", job_id, exc)
        raise
    except Exception as exc:
        logger.exception("视频合成异常: job_id=%s", job_id)
        try:
            job_service.fail_job(job_id, str(exc)[:500])
        except Exception:
            logger.exception("更新 Job 失败状态时出错")
        raise self.retry(exc=exc, countdown=60)
    finally:
        db.close()
        # 清理临时文件
        try:
            _output_dir = os.environ.get("VIDEO_OUTPUT_DIR", os.path.join(tempfile.gettempdir(), "video_output"))
            output_path = os.path.join(_output_dir, f"{job_id}.mp4")
            if Path(output_path).exists():
                Path(output_path).unlink()
        except Exception as e:
            logger.warning(f"Operation failed in apps/worker/worker_app/tasks/compose_video.py: {e}", exc_info=True)
