<?php

namespace App\Jobs;

use App\Models\TutorialVideo;
use App\Services\GoogleCloudStorageService;
use App\Services\Video\BgmService;
use App\Services\Video\ScreenAnalysisService;
use App\Services\Video\SlideAnnotationService;
use App\Services\Video\TtsService;
use App\Services\Video\TutorialAssemblyService;
use App\Services\Video\VideoAssemblyService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;

class TutorialVideoJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public int $timeout = 900; // 15분

    public int $tries = 1;

    private int $tutorialVideoId;

    public function __construct(int $tutorialVideoId)
    {
        $this->tutorialVideoId = $tutorialVideoId;
        $this->onQueue('mng');
    }

    public function handle(
        ScreenAnalysisService $screenAnalysis,
        SlideAnnotationService $slideAnnotation,
        TtsService $tts,
        BgmService $bgm,
        VideoAssemblyService $videoAssembly,
        TutorialAssemblyService $tutorialAssembly,
        GoogleCloudStorageService $gcs
    ): void {
        $tutorial = TutorialVideo::withoutGlobalScopes()->find($this->tutorialVideoId);

        if (! $tutorial) {
            Log::error('TutorialVideoJob: 레코드를 찾을 수 없음', ['id' => $this->tutorialVideoId]);

            return;
        }

        $workDir = storage_path("app/tutorial_gen/{$tutorial->id}");
        if (! is_dir($workDir)) {
            mkdir($workDir, 0755, true);
        }

        $totalCost = 0.0;

        try {
            // === Step 1: AI 스크린샷 분석 (10%) ===
            $tutorial->updateProgress(TutorialVideo::STATUS_ANALYZING, 5, 'AI 스크린샷 분석 중...');

            $analysisData = $tutorial->analysis_data;

            if (empty($analysisData)) {
                $screenshots = $tutorial->screenshots ?? [];

                if (empty($screenshots)) {
                    $tutorial->markFailed('업로드된 스크린샷이 없습니다');

                    return;
                }

                $analysisData = $screenAnalysis->analyzeScreenshots($screenshots);

                if (empty($analysisData)) {
                    $tutorial->markFailed('스크린샷 분석에 실패했습니다');

                    return;
                }

                $tutorial->update(['analysis_data' => $analysisData]);
                $totalCost += 0.01; // Gemini Vision
            } else {
                // DB 캐시된 기존 형식도 steps[] 형식으로 변환
                $analysisData = array_map(
                    fn ($screen) => $screenAnalysis->ensureStepsFormatPublic($screen),
                    $analysisData
                );
            }

            $tutorial->updateProgress(TutorialVideo::STATUS_ANALYZING, 10, 'AI 분석 완료');

            // === Step 2: 어노테이션 슬라이드 생성 (30%) ===
            $tutorial->updateProgress(TutorialVideo::STATUS_GENERATING_SLIDES, 15, '슬라이드 생성 중...');

            $slidePaths = [];
            $durations = [];
            $scenes = [];
            $screenshots = $tutorial->screenshots ?? [];

            // 전체 step 수 계산 (진행 배지용)
            $totalSteps = 0;
            foreach ($analysisData as $screen) {
                $totalSteps += count($screen['steps'] ?? []);
            }

            // 인트로 슬라이드 생성
            $introPath = "{$workDir}/slide_intro.png";
            $introTitle = $tutorial->title ?? 'SAM 사용자 매뉴얼';
            $introResult = $slideAnnotation->createIntroSlide($introTitle, $introPath);
            if ($introResult) {
                $slidePaths[] = $introResult;
                $durations[] = 3;
                $scenes[] = [
                    'scene_number' => 1,
                    'narration' => "{$introTitle}. SAM 사용법을 안내합니다.",
                    'duration' => 3,
                ];
            }

            // 중첩 루프: screen → steps
            $globalStepNum = 0;
            foreach ($analysisData as $i => $screen) {
                $screenNum = $screen['screen_number'] ?? ($i + 1);
                $imagePath = $screenshots[$i] ?? null;
                $steps = $screen['steps'] ?? [];

                if (! $imagePath || ! file_exists($imagePath)) {
                    Log::warning("TutorialVideoJob: 스크린샷 없음 - index {$i}");

                    continue;
                }

                foreach ($steps as $step) {
                    $globalStepNum++;
                    $stepNum = $step['step_number'] ?? $globalStepNum;
                    $sceneNumber = $screenNum * 100 + $stepNum;

                    $outputSlide = "{$workDir}/slide_{$sceneNumber}.png";

                    $result = $slideAnnotation->annotateSlideWithSpotlight(
                        $imagePath,
                        $step['focused_element'] ?? null,
                        $globalStepNum,
                        $totalSteps,
                        $step['narration'] ?? "단계 {$stepNum}",
                        $outputSlide
                    );

                    if ($result) {
                        $slidePaths[] = $result;
                        $duration = $step['duration'] ?? 6;
                        $durations[] = $duration;
                        $scenes[] = [
                            'scene_number' => $sceneNumber,
                            'narration' => $step['narration'] ?? '',
                            'duration' => $duration,
                        ];
                    }
                }

                $progress = 15 + (int) (($i + 1) / count($analysisData) * 13);
                $tutorial->updateProgress(TutorialVideo::STATUS_GENERATING_SLIDES, $progress, "화면 {$screenNum} 슬라이드 생성 완료");
            }

            // 아웃트로 슬라이드 생성
            $outroPath = "{$workDir}/slide_outro.png";
            $outroResult = $slideAnnotation->createOutroSlide($introTitle, $outroPath);
            if ($outroResult) {
                $slidePaths[] = $outroResult;
                $durations[] = 3;
                $scenes[] = [
                    'scene_number' => 999,
                    'narration' => '이상으로 안내를 마칩니다. 감사합니다.',
                    'duration' => 3,
                ];
            }

            if (count($slidePaths) <= 2) { // 인트로+아웃트로만 있으면 실패
                $tutorial->markFailed('슬라이드 생성에 실패했습니다');

                return;
            }

            $tutorial->update(['slides_data' => [
                'slide_paths' => $slidePaths,
                'durations' => $durations,
                'scenes' => $scenes,
            ]]);

            $tutorial->updateProgress(TutorialVideo::STATUS_GENERATING_SLIDES, 30, '슬라이드 생성 완료');

            // === Step 3: TTS 나레이션 생성 (50%) ===
            $tutorial->updateProgress(TutorialVideo::STATUS_GENERATING_TTS, 35, '나레이션 생성 중...');

            $narrationPaths = $tts->synthesizeScenes($scenes, $workDir);
            $totalCost += 0.01; // TTS

            $tutorial->updateProgress(TutorialVideo::STATUS_GENERATING_TTS, 50, '나레이션 생성 완료');

            // 실제 나레이션 길이 측정 + duration 보정
            $narrationDurations = [];
            foreach ($narrationPaths as $sceneNum => $path) {
                $actualDuration = $videoAssembly->getAudioDuration($path);
                if ($actualDuration > 0) {
                    $narrationDurations[$sceneNum] = $actualDuration;

                    // 나레이션이 슬라이드보다 길면 duration 보정
                    foreach ($durations as $di => &$dur) {
                        if (($scenes[$di]['scene_number'] ?? 0) === $sceneNum) {
                            if ($actualDuration > $dur) {
                                $dur = ceil($actualDuration) + 1;
                                $scenes[$di]['duration'] = $dur;
                            }
                        }
                    }
                    unset($dur);
                }
            }

            // 나레이션 합치기
            $concatNarrationPath = "{$workDir}/narration_full.mp3";
            $narrationPath = $videoAssembly->concatNarrations($narrationPaths, $scenes, $concatNarrationPath);

            // === Step 4: BGM 생성 (65%) ===
            $tutorial->updateProgress(TutorialVideo::STATUS_GENERATING_BGM, 55, 'BGM 생성 중...');

            $totalDuration = array_sum($durations);
            $bgmPath = "{$workDir}/bgm.mp3";

            // 튜토리얼용 차분한 BGM
            $bgmResult = $bgm->generateWithLyria('calm, instructional, soft background', $totalDuration + 5, $bgmPath);

            if (! $bgmResult) {
                $bgmResult = $bgm->select('calm', $bgmPath);
            }
            if (! $bgmResult) {
                $bgmResult = $bgm->generateAmbient('calm', $totalDuration + 5, $bgmPath);
            }

            $totalCost += 0.06; // Lyria

            $tutorial->updateProgress(TutorialVideo::STATUS_GENERATING_BGM, 65, 'BGM 생성 완료');

            // === Step 5: 최종 합성 (80%) ===
            $tutorial->updateProgress(TutorialVideo::STATUS_ASSEMBLING, 70, '영상 합성 중...');

            // 자막 비활성화: 슬라이드 하단 캡션바에 나레이션이 이미 표시되므로 ASS 자막 불필요

            // 최종 MP4 합성
            $finalOutputPath = "{$workDir}/final_tutorial.mp4";
            $result = $tutorialAssembly->assembleFromImages(
                $slidePaths,
                $durations,
                $narrationPath,
                $bgmResult,
                null,
                $finalOutputPath
            );

            if (! $result || ! file_exists($finalOutputPath)) {
                $tutorial->markFailed('영상 합성에 실패했습니다');

                return;
            }

            $tutorial->updateProgress(TutorialVideo::STATUS_ASSEMBLING, 80, '영상 합성 완료');

            // === Step 6: GCS 업로드 (95%) ===
            $tutorial->updateProgress(TutorialVideo::STATUS_ASSEMBLING, 85, 'GCS 업로드 중...');

            $gcsPath = null;
            if ($gcs->isAvailable()) {
                $objectName = "tutorials/{$tutorial->tenant_id}/{$tutorial->id}/tutorial_".date('Ymd_His').'.mp4';
                $gcsPath = $gcs->upload($finalOutputPath, $objectName);
            }

            $tutorial->updateProgress(TutorialVideo::STATUS_ASSEMBLING, 95, '업로드 완료');

            // === Step 7: 완료 (100%) ===
            // analysis_data에 영상 저장 경로 메타데이터 추가
            $analysisWithMeta = $tutorial->analysis_data ?? $analysisData;
            if (is_array($analysisWithMeta)) {
                $analysisWithMeta['_output'] = [
                    'completed_at' => now()->toIso8601String(),
                    'output_path' => $finalOutputPath,
                    'gcs_path' => $gcsPath,
                    'cost_usd' => round($totalCost, 4),
                    'total_slides' => count($slidePaths),
                    'total_duration' => $totalDuration,
                ];
            }

            $tutorial->update([
                'status' => TutorialVideo::STATUS_COMPLETED,
                'progress' => 100,
                'current_step' => '완료',
                'output_path' => $finalOutputPath,
                'gcs_path' => $gcsPath,
                'cost_usd' => $totalCost,
                'analysis_data' => $analysisWithMeta,
            ]);

            Log::info('TutorialVideoJob: 완료', [
                'id' => $tutorial->id,
                'output' => $finalOutputPath,
                'gcs' => $gcsPath,
                'cost' => $totalCost,
            ]);

        } catch (\Exception $e) {
            Log::error('TutorialVideoJob: 예외 발생', [
                'id' => $tutorial->id,
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
            ]);

            $tutorial->markFailed($e->getMessage());
        }
    }
}
