using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using SaasMediaWorker.Configuration;
using SaasMediaWorker.Models;

namespace SaasMediaWorker.Services;

/// <summary>
/// Video render orchestrator — Tüm medya üretim ve birleştirme pipeline'ını yönetir.
///
/// Pipeline Adımları:
/// 1. Temp dizin oluştur
/// 2. Her sahne için: Higgsfield (video klip) + TTS (narration)
/// 3. AudioCraft MusicGen (background müzik) — fallback: Suno
/// 4. AudioCraft AudioGen (sahne bazlı ambient sesler)
/// 5. Her sahne için: video + narration + ambient merge + altyazı
/// 6. Tüm sahneleri birleştir + müzik mix (3 katmanlı ses)
/// 7. S3'e yükle
/// 8. Temp dizini temizle
/// </summary>
public class VideoRenderPipeline
{
    private readonly ILogger<VideoRenderPipeline> _logger;
    private readonly HiggsFieldService _higgsField;
    private readonly TtsService _tts;
    private readonly SunoMusicService _sunoMusic;
    private readonly AudioCraftService _audioCraft;
    private readonly FFmpegService _ffmpeg;
    private readonly S3StorageService _s3;
    private readonly FFmpegSettings _ffmpegSettings;

    public VideoRenderPipeline(
        ILogger<VideoRenderPipeline> logger,
        HiggsFieldService higgsField,
        TtsService tts,
        SunoMusicService sunoMusic,
        AudioCraftService audioCraft,
        FFmpegService ffmpeg,
        S3StorageService s3,
        IOptions<FFmpegSettings> ffmpegSettings)
    {
        _logger = logger;
        _higgsField = higgsField;
        _tts = tts;
        _sunoMusic = sunoMusic;
        _audioCraft = audioCraft;
        _ffmpeg = ffmpeg;
        _s3 = s3;
        _ffmpegSettings = ffmpegSettings.Value;
    }

    /// <summary>
    /// Tam render pipeline'ını çalıştırır.
    /// Giriş: VideoGenerationJob (Redis'ten alınan)
    /// Çıkış: Final video'nun S3 URL'i
    /// </summary>
    public async Task<string> ExecuteAsync(
        VideoGenerationJob job,
        Func<int, string, Task> progressCallback,
        CancellationToken ct)
    {
        var projectDir = Path.Combine(_ffmpegSettings.TempDirectory, job.ProjectId);
        Directory.CreateDirectory(projectDir);

        var allMediaFiles = new List<GeneratedMediaFile>();

        try
        {
            var scenes = job.Scenes.OrderBy(s => s.Order).ToList();
            var totalSteps = scenes.Count * 3 + 4; // (video+tts+ambient per scene) + music + merge + upload + finalize
            var completedSteps = 0;

            // ═══════════════════════════════════════
            // ADIM 1: Her sahne için video klip üret
            // ═══════════════════════════════════════
            _logger.LogInformation(
                "📹 Adım 1/5: Video klip üretimi — {Count} sahne", scenes.Count);

            var videoTasks = new List<Task<GeneratedMediaFile>>();
            foreach (var scene in scenes)
            {
                videoTasks.Add(GenerateVideoClipWithProgress(
                    scene, projectDir, job.AspectRatio,
                    () =>
                    {
                        completedSteps++;
                        var progress = (int)(completedSteps / (double)totalSteps * 60); // Video %0-60
                        return progressCallback(Math.Min(progress, 60), "VIDEO_GENERATION");
                    }, ct));
            }

            var videoResults = await Task.WhenAll(videoTasks);
            allMediaFiles.AddRange(videoResults);

            // ═══════════════════════════════════════
            // ADIM 2: Her sahne için TTS narration üret
            // ═══════════════════════════════════════
            _logger.LogInformation("🎙️ Adım 2/5: TTS narration üretimi");

            var voiceStyle = job.ScriptJson?.VoiceStyle ?? "Deep authoritative male voice";
            var ttsTasks = new List<Task<GeneratedMediaFile>>();

            foreach (var scene in scenes)
            {
                ttsTasks.Add(GenerateTtsWithProgress(
                    scene, projectDir, voiceStyle,
                    () =>
                    {
                        completedSteps++;
                        var progress = 60 + (int)(completedSteps / (double)totalSteps * 10); // TTS %60-70
                        return progressCallback(Math.Min(progress, 70), "TTS_GENERATION");
                    }, ct));
            }

            var ttsResults = await Task.WhenAll(ttsTasks);
            allMediaFiles.AddRange(ttsResults);

            // ═══════════════════════════════════════
            // ADIM 3: Background müzik üret
            // ═══════════════════════════════════════
            _logger.LogInformation("🎵 Adım 3/6: Background müzik üretimi (AudioCraft MusicGen)");
            await progressCallback(72, "MUSIC_GENERATION");

            var musicPrompt = job.ScriptJson?.MusicPrompt
                ?? "Cinematic orchestral, mysterious, slow build, 80 BPM, strings and piano";

            // Teknik parametreleri ScriptPayload'dan al
            MusicTechnicalParams? musicTechnical = null;
            if (job.ScriptJson?.MusicTechnical != null)
            {
                musicTechnical = new MusicTechnicalParams
                {
                    Bpm = job.ScriptJson.MusicTechnical.Bpm,
                    Key = job.ScriptJson.MusicTechnical.Key,
                    Instruments = job.ScriptJson.MusicTechnical.Instruments,
                    EmotionalArc = job.ScriptJson.MusicTechnical.EmotionalArc
                };
            }

            GeneratedMediaFile? musicFile = null;

            // Önce AudioCraft MusicGen dene, başarısız olursa Suno fallback
            try
            {
                musicFile = await _audioCraft.GenerateMusicAsync(
                    musicPrompt, musicTechnical, job.TargetDuration, projectDir, ct);
                allMediaFiles.Add(musicFile);
                _logger.LogInformation("✅ MusicGen başarılı — AudioCraft ile müzik üretildi");
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "MusicGen başarısız — Suno fallback deneniyor");

                try
                {
                    musicFile = await _sunoMusic.GenerateMusicAsync(
                        musicPrompt, job.TargetDuration, projectDir, ct);
                    allMediaFiles.Add(musicFile);
                    _logger.LogInformation("✅ Suno fallback başarılı");
                }
                catch (Exception sunoEx)
                {
                    _logger.LogWarning(sunoEx, "Suno da başarısız — müziksiz devam ediliyor");
                }
            }

            await progressCallback(78, "MUSIC_GENERATION");

            // ═══════════════════════════════════════
            // ADIM 4: AudioGen — Sahne bazlı ambient sesler
            // ═══════════════════════════════════════
            _logger.LogInformation("🔊 Adım 4/6: AudioGen ambient ses efektleri");
            await progressCallback(79, "AMBIENT_GENERATION");

            var ambientFiles = new List<GeneratedMediaFile>();
            try
            {
                ambientFiles = await _audioCraft.GenerateAllAmbientSoundsAsync(
                    scenes, projectDir, ct);
                allMediaFiles.AddRange(ambientFiles);
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "Ambient ses üretimi başarısız — ambientsiz devam ediliyor");
            }

            await progressCallback(82, "AMBIENT_GENERATION");

            // ═══════════════════════════════════════
            // ADIM 5: FFmpeg — Birleştirme, ambient overlay ve altyazı
            // ═══════════════════════════════════════
            _logger.LogInformation("🎬 Adım 5/6: FFmpeg render — merge + ambient + subtitle + finalize");
            await progressCallback(83, "MEDIA_MERGE");

            var mergedScenePaths = new List<string>();

            foreach (var scene in scenes)
            {
                var videoFile = videoResults.FirstOrDefault(v =>
                    v.SceneOrder == scene.Order && v.Type == MediaFileType.VideoClip);
                var ttsFile = ttsResults.FirstOrDefault(t =>
                    t.SceneOrder == scene.Order && t.Type == MediaFileType.AudioNarration);

                if (videoFile == null)
                {
                    _logger.LogWarning("Sahne {Order} için video bulunamadı, atlanıyor", scene.Order);
                    continue;
                }

                var currentPath = videoFile.LocalPath;

                // Video + Narration merge
                if (ttsFile != null)
                {
                    currentPath = await _ffmpeg.MergeVideoWithNarrationAsync(
                        currentPath, ttsFile.LocalPath, scene.Order, projectDir, ct);
                }

                // Ambient ses overlay (AudioGen)
                var ambientFile = ambientFiles.FirstOrDefault(a =>
                    a.SceneOrder == scene.Order && a.Type == MediaFileType.AudioAmbient);
                if (ambientFile != null)
                {
                    currentPath = await _ffmpeg.OverlayAmbientAudioAsync(
                        currentPath, ambientFile.LocalPath, scene.Order, projectDir, ct);
                }

                // Altyazı ekle
                if (!string.IsNullOrEmpty(scene.SubtitleText))
                {
                    currentPath = await _ffmpeg.AddSubtitlesAsync(
                        currentPath, scene.SubtitleText, scene.Order, projectDir, ct);
                }

                mergedScenePaths.Add(currentPath);
            }

            await progressCallback(88, "MEDIA_MERGE");

            // Final concatenation + music mix
            var finalLocalPath = await _ffmpeg.ConcatenateAndFinalize(
                mergedScenePaths,
                musicFile?.LocalPath,
                projectDir,
                job.ProjectId,
                job.TargetDuration,
                ct);

            allMediaFiles.Add(new GeneratedMediaFile
            {
                Type = MediaFileType.FinalVideo,
                LocalPath = finalLocalPath,
                FileSizeBytes = new FileInfo(finalLocalPath).Length,
                DurationSeconds = job.TargetDuration,
                MimeType = "video/mp4"
            });

            await progressCallback(92, "MEDIA_MERGE");

            // ═══════════════════════════════════════
            // ADIM 6: S3'e yükle
            // ═══════════════════════════════════════
            _logger.LogInformation("☁️ Adım 6/6: S3 yükleme");
            await progressCallback(94, "UPLOAD");

            // Tüm medya dosyalarını yükle
            await _s3.UploadAllMediaAsync(job.ProjectId, allMediaFiles, ct);

            // Final videoyu yükle
            var finalVideoUrl = await _s3.UploadFinalVideoAsync(
                finalLocalPath, job.ProjectId, ct);

            await progressCallback(98, "FINALIZATION");

            _logger.LogInformation(
                "🎉 Pipeline tamamlandı — Project: {ProjectId}, URL: {Url}",
                job.ProjectId, finalVideoUrl);

            return finalVideoUrl;
        }
        finally
        {
            // Temp dizini temizle (bellek/disk tasarrufu)
            CleanupTempDirectory(projectDir);
        }
    }

    private async Task<GeneratedMediaFile> GenerateVideoClipWithProgress(
        ScenePayload scene, string outputDir, string aspectRatio,
        Func<Task> onComplete, CancellationToken ct)
    {
        var result = await _higgsField.GenerateVideoClipAsync(
            scene, outputDir, aspectRatio, ct);
        await onComplete();
        return result;
    }

    private async Task<GeneratedMediaFile> GenerateTtsWithProgress(
        ScenePayload scene, string outputDir, string voiceStyle,
        Func<Task> onComplete, CancellationToken ct)
    {
        var result = await _tts.GenerateNarrationAsync(
            scene, outputDir, voiceStyle, ct);
        await onComplete();
        return result;
    }

    private void CleanupTempDirectory(string path)
    {
        try
        {
            if (Directory.Exists(path))
            {
                Directory.Delete(path, recursive: true);
                _logger.LogInformation("Temp dizin temizlendi: {Path}", path);
            }
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "Temp dizin temizlenemedi: {Path}", path);
        }
    }
}
