using System.Diagnostics;
using System.Globalization;
using System.Text;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using SaasMediaWorker.Configuration;
using SaasMediaWorker.Models;

namespace SaasMediaWorker.Services;

/// <summary>
/// FFmpeg ARM64 Wrapper — Video render, birleştirme ve altyazı ekleme.
///
/// Neden FFmpeg (Canva API yerine)?
/// - Canva API rate-limit'li ve ücretli. FFmpeg sıfır maliyet.
/// - ARM64 Raspberry Pi'da native çalışır (Alpine paketi ile).
/// - Text overlay, audio merge, transition'lar için endüstri standardı.
/// - Offline çalışır — dış API bağımlılığı yok.
///
/// ARM64 önemli notlar:
/// - Hardware acceleration yok (RPi'da GPU desteği kısıtlı) → software encode
/// - libx264 + aac codec kullanılıyor (ARM64'te stabil)
/// - Memory sınırı: maxmuxqueuesize ile OOM koruması
/// </summary>
public class FFmpegService
{
    private readonly ILogger<FFmpegService> _logger;
    private readonly FFmpegSettings _settings;

    public FFmpegService(
        ILogger<FFmpegService> logger,
        IOptions<FFmpegSettings> settings)
    {
        _logger = logger;
        _settings = settings.Value;

        // Temp dizinini oluştur
        Directory.CreateDirectory(_settings.TempDirectory);
    }

    /// <summary>
    /// Her sahnenin video klibine narration ses dosyasını overlay eder.
    /// Çıktı: sync edilmiş video+narration dosyası
    /// </summary>
    public async Task<string> MergeVideoWithNarrationAsync(
        string videoPath,
        string narrationPath,
        int sceneOrder,
        string outputDirectory,
        CancellationToken ct)
    {
        var outputPath = Path.Combine(outputDirectory, $"scene_{sceneOrder:D2}_merged.mp4");

        // Video'ya narration ses'i ekle, video ses'i kapat
        var args = new StringBuilder();
        args.Append($"-y -i \"{videoPath}\" -i \"{narrationPath}\" ");
        args.Append("-map 0:v:0 -map 1:a:0 ");
        args.Append("-c:v libx264 -preset fast -crf 23 ");
        args.Append("-c:a aac -b:a 128k ");
        args.Append("-movflags +faststart ");
        args.Append("-shortest "); // Kısa olan medyaya göre kes
        args.Append($"\"{outputPath}\"");

        await RunFFmpegAsync(args.ToString(), ct);

        _logger.LogInformation("Video+Narration merge — Sahne {Order}: {Path}",
            sceneOrder, outputPath);

        return outputPath;
    }

    /// <summary>
    /// Sahne videolarına altyazı ekler (burn-in).
    /// ASS formatında dinamik altyazı dosyası oluşturur.
    /// </summary>
    public async Task<string> AddSubtitlesAsync(
        string videoPath,
        string subtitleText,
        int sceneOrder,
        string outputDirectory,
        CancellationToken ct)
    {
        // ASS altyazı dosyası oluştur
        var assPath = Path.Combine(outputDirectory, $"scene_{sceneOrder:D2}_sub.ass");
        var assContent = GenerateAssSubtitle(subtitleText, 0, 60);
        await File.WriteAllTextAsync(assPath, assContent, ct);

        var outputPath = Path.Combine(outputDirectory, $"scene_{sceneOrder:D2}_subtitled.mp4");

        var escapedAssPath = assPath.Replace(":", "\\:").Replace("'", "\\'");

        var args = new StringBuilder();
        args.Append($"-y -i \"{videoPath}\" ");
        args.Append($"-vf \"ass={escapedAssPath}\" ");
        args.Append("-c:v libx264 -preset fast -crf 23 ");
        args.Append("-c:a copy ");
        args.Append("-movflags +faststart ");
        args.Append($"\"{outputPath}\"");

        await RunFFmpegAsync(args.ToString(), ct);

        return outputPath;
    }

    /// <summary>
    /// Tüm sahne videolarını sırayla birleştirir ve background müzik ekler.
    /// Bu, render pipeline'ının son adımıdır.
    /// </summary>
    public async Task<string> ConcatenateAndFinalize(
        List<string> sceneVideoPaths,
        string? musicPath,
        string outputDirectory,
        string projectId,
        double targetDuration,
        CancellationToken ct)
    {
        _logger.LogInformation(
            "🎬 Final render başlıyor — {Count} sahne birleştiriliyor",
            sceneVideoPaths.Count);

        // 1. Concat dosyası oluştur (FFmpeg concat demuxer)
        var concatListPath = Path.Combine(outputDirectory, "concat_list.txt");
        var concatContent = string.Join("\n",
            sceneVideoPaths.Select(p => $"file '{p}'"));
        await File.WriteAllTextAsync(concatListPath, concatContent, ct);

        // 2. Önce videoları birleştir
        var concatenatedPath = Path.Combine(outputDirectory, "concatenated.mp4");
        var concatArgs = new StringBuilder();
        concatArgs.Append($"-y -f concat -safe 0 -i \"{concatListPath}\" ");
        concatArgs.Append("-c:v libx264 -preset fast -crf 22 ");
        concatArgs.Append("-c:a aac -b:a 128k ");
        concatArgs.Append("-movflags +faststart ");
        concatArgs.Append($"\"{concatenatedPath}\"");

        await RunFFmpegAsync(concatArgs.ToString(), ct);

        // 3. Background müzik varsa ekle
        var finalPath = Path.Combine(outputDirectory, $"final_{projectId}.mp4");

        if (!string.IsNullOrEmpty(musicPath) && File.Exists(musicPath))
        {
            // Narration sesi (stream 0:a) + müzik (stream 1:a) mix
            // Müzik %20 volume (narration'ın altında kalması için)
            var musicArgs = new StringBuilder();
            musicArgs.Append($"-y -i \"{concatenatedPath}\" -i \"{musicPath}\" ");
            musicArgs.Append("-filter_complex \"");
            musicArgs.Append("[1:a]volume=0.20[music];");
            musicArgs.Append("[0:a][music]amix=inputs=2:duration=first:dropout_transition=3[aout]\" ");
            musicArgs.Append("-map 0:v:0 -map \"[aout]\" ");
            musicArgs.Append("-c:v copy ");
            musicArgs.Append("-c:a aac -b:a 192k ");
            musicArgs.Append("-movflags +faststart ");
            musicArgs.Append($"-t {targetDuration.ToString("F1", CultureInfo.InvariantCulture)} ");
            musicArgs.Append($"\"{finalPath}\"");

            await RunFFmpegAsync(musicArgs.ToString(), ct);
        }
        else
        {
            // Müzik yoksa sadece süreyi kes
            var trimArgs = $"-y -i \"{concatenatedPath}\" -c copy " +
                           $"-t {targetDuration.ToString("F1", CultureInfo.InvariantCulture)} " +
                           $"-movflags +faststart \"{finalPath}\"";
            await RunFFmpegAsync(trimArgs, ct);
        }

        // Dosya boyutunu kontrol et
        var fileInfo = new FileInfo(finalPath);
        _logger.LogInformation(
            "✅ Final render tamamlandı — {Path} ({Size:F1} MB)",
            finalPath, fileInfo.Length / (1024.0 * 1024.0));

        return finalPath;
    }

    /// <summary>
    /// Sahne videosuna AudioGen ambient ses efektini overlay eder.
    /// Volume: -22dB (narration ve müziğin altında, atmosferik katman).
    /// AudioCraft AudioGen'den gelen ses dosyası ile birleştirir.
    /// </summary>
    public async Task<string> OverlayAmbientAudioAsync(
        string videoPath,
        string ambientAudioPath,
        int sceneOrder,
        string outputDirectory,
        CancellationToken ct)
    {
        var outputPath = Path.Combine(outputDirectory, $"scene_{sceneOrder:D2}_ambient.mp4");

        var args = new StringBuilder();
        args.Append($"-y -i \"{videoPath}\" -i \"{ambientAudioPath}\" ");
        args.Append("-filter_complex \"");
        // Ambient ses: %8 volume (-22dB) — çok hafif arka plan katmanı
        args.Append("[1:a]volume=0.08,aformat=sample_rates=44100:channel_layouts=mono[amb];");
        // Mevcut ses + ambient mix
        args.Append("[0:a][amb]amix=inputs=2:duration=first:dropout_transition=2[aout]\" ");
        args.Append("-map 0:v:0 -map \"[aout]\" ");
        args.Append("-c:v copy ");
        args.Append("-c:a aac -b:a 128k ");
        args.Append("-movflags +faststart ");
        args.Append($"\"{outputPath}\"");

        await RunFFmpegAsync(args.ToString(), ct);

        _logger.LogInformation(
            "🔊 Ambient overlay — Sahne {Order}: {Path}", sceneOrder, outputPath);

        return outputPath;
    }

    /// <summary>
    /// Video dosyasının süresini FFprobe ile ölçer.
    /// </summary>
    public async Task<double> GetVideoDurationAsync(string videoPath, CancellationToken ct)
    {
        var args = $"-v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 \"{videoPath}\"";

        var result = await RunProcessAsync(_settings.FfprobePath, args, ct);
        if (double.TryParse(result.Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var duration))
        {
            return duration;
        }
        return 0;
    }

    /// <summary>
    /// ASS formatında altyazı dosyası üretir.
    /// Mobile-first: büyük font, ortalanmış, yarı-şeffaf arka plan.
    /// </summary>
    private static string GenerateAssSubtitle(string text, double startSeconds, double endSeconds)
    {
        var startTime = TimeSpan.FromSeconds(startSeconds).ToString(@"h\:mm\:ss\.ff");
        var endTime = TimeSpan.FromSeconds(endSeconds).ToString(@"h\:mm\:ss\.ff");

        // Satır uzunluğunu sınırla (mobil okunabilirlik)
        var words = text.Split(' ');
        var lines = new List<string>();
        var currentLine = new StringBuilder();

        foreach (var word in words)
        {
            if (currentLine.Length + word.Length + 1 > 30)
            {
                lines.Add(currentLine.ToString().Trim());
                currentLine.Clear();
            }
            currentLine.Append(word).Append(' ');
        }
        if (currentLine.Length > 0)
            lines.Add(currentLine.ToString().Trim());

        var formattedText = string.Join("\\N", lines);

        return $@"[Script Info]
Title: ContentGen AI Subtitles
ScriptType: v4.00+
PlayResX: 1080
PlayResY: 1920
WrapStyle: 0

[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,DejaVu Sans,56,&H00FFFFFF,&H000000FF,&H00000000,&H80000000,-1,0,0,0,100,100,0,0,1,3,1,2,40,40,120,1

[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,{startTime},{endTime},Default,,0,0,0,,{formattedText}
";
    }

    /// <summary>
    /// FFmpeg process'ini çalıştırır ve çıktısını izler.
    /// ARM64 uyumlu — hardware acceleration kullanmaz.
    /// </summary>
    private async Task RunFFmpegAsync(string arguments, CancellationToken ct)
    {
        await RunProcessAsync(_settings.BinaryPath, arguments, ct);
    }

    private async Task<string> RunProcessAsync(string executable, string arguments, CancellationToken ct)
    {
        _logger.LogDebug("Process: {Exe} {Args}", executable, arguments);

        using var process = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = executable,
                Arguments = arguments,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                UseShellExecute = false,
                CreateNoWindow = true,
            }
        };

        var stdout = new StringBuilder();
        var stderr = new StringBuilder();

        process.OutputDataReceived += (_, e) =>
        {
            if (e.Data != null) stdout.AppendLine(e.Data);
        };
        process.ErrorDataReceived += (_, e) =>
        {
            if (e.Data != null) stderr.AppendLine(e.Data);
        };

        process.Start();
        process.BeginOutputReadLine();
        process.BeginErrorReadLine();

        await process.WaitForExitAsync(ct);

        if (process.ExitCode != 0)
        {
            var errorOutput = stderr.ToString();
            _logger.LogError("FFmpeg hata (exit: {Code}): {Error}",
                process.ExitCode, errorOutput[..Math.Min(500, errorOutput.Length)]);
            throw new InvalidOperationException(
                $"FFmpeg başarısız (exit: {process.ExitCode}): {errorOutput[..Math.Min(200, errorOutput.Length)]}");
        }

        return stdout.ToString();
    }
}
