using Amazon.S3;
using Amazon.S3.Model;
using Amazon.S3.Transfer;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using SaasMediaWorker.Configuration;
using SaasMediaWorker.Models;

namespace SaasMediaWorker.Services;

/// <summary>
/// S3/Cloudflare R2 depolama servisi.
/// Üretilen medya dosyalarını nesne depolamaya yükler.
///
/// Neden Cloudflare R2?
/// - Egress (çıkış) ücretsiz — global SaaS için büyük maliyet avantajı
/// - S3 uyumlu API — AWS SDK ile doğrudan çalışır
/// - ARM64 uyumlu — AWS SDK .NET tamamen cross-platform
/// </summary>
public class S3StorageService
{
    private readonly ILogger<S3StorageService> _logger;
    private readonly S3Settings _settings;
    private readonly AmazonS3Client _s3Client;

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

        var config = new AmazonS3Config
        {
            ServiceURL = _settings.Endpoint,
            ForcePathStyle = true
        };

        _s3Client = new AmazonS3Client(
            _settings.AccessKey,
            _settings.SecretKey,
            config);
    }

    /// <summary>
    /// Tek bir dosyayı S3/R2'ye yükler.
    /// Büyük dosyalar (>5MB) otomatik multipart upload kullanır.
    /// </summary>
    public async Task<(string url, string key)> UploadFileAsync(
        string localPath,
        string projectId,
        string fileName,
        string contentType,
        CancellationToken ct)
    {
        var key = $"projects/{projectId}/{DateTime.UtcNow:yyyy/MM/dd}/{fileName}";

        _logger.LogInformation(
            "☁️ S3 yükleme başlıyor — Key: {Key}, Dosya: {File}",
            key, Path.GetFileName(localPath));

        var fileInfo = new FileInfo(localPath);

        if (fileInfo.Length > 5 * 1024 * 1024) // 5MB üstü → multipart
        {
            await UploadMultipartAsync(localPath, key, contentType, ct);
        }
        else
        {
            var request = new PutObjectRequest
            {
                BucketName = _settings.BucketName,
                Key = key,
                FilePath = localPath,
                ContentType = contentType,
                DisablePayloadSigning = true,
            };

            await _s3Client.PutObjectAsync(request, ct);
        }

        var publicUrl = string.IsNullOrEmpty(_settings.PublicBaseUrl)
            ? $"{_settings.Endpoint}/{_settings.BucketName}/{key}"
            : $"{_settings.PublicBaseUrl.TrimEnd('/')}/{key}";

        _logger.LogInformation(
            "✅ S3 yükleme tamamlandı — {Key} ({Size} bytes)",
            key, fileInfo.Length);

        return (publicUrl, key);
    }

    /// <summary>
    /// Render pipeline sonucu üretilen tüm medya dosyalarını toplu yükler.
    /// </summary>
    public async Task<List<GeneratedMediaFile>> UploadAllMediaAsync(
        string projectId,
        List<GeneratedMediaFile> mediaFiles,
        CancellationToken ct)
    {
        var uploadedFiles = new List<GeneratedMediaFile>();

        foreach (var media in mediaFiles)
        {
            if (!File.Exists(media.LocalPath))
            {
                _logger.LogWarning("Dosya bulunamadı, atlanıyor: {Path}", media.LocalPath);
                continue;
            }

            var fileName = Path.GetFileName(media.LocalPath);
            var (url, key) = await UploadFileAsync(
                media.LocalPath, projectId, fileName, media.MimeType, ct);

            media.S3Url = url;
            media.S3Key = key;
            media.FileSizeBytes = new FileInfo(media.LocalPath).Length;

            uploadedFiles.Add(media);
        }

        _logger.LogInformation(
            "Toplu yükleme tamamlandı — {Count}/{Total} dosya yüklendi",
            uploadedFiles.Count, mediaFiles.Count);

        return uploadedFiles;
    }

    /// <summary>
    /// Final videoyu yükler ve public URL döner.
    /// </summary>
    public async Task<string> UploadFinalVideoAsync(
        string localPath,
        string projectId,
        CancellationToken ct)
    {
        var fileName = $"final_video_{DateTime.UtcNow:yyyyMMdd_HHmmss}.mp4";
        var (url, _) = await UploadFileAsync(
            localPath, projectId, fileName, "video/mp4", ct);
        return url;
    }

    private async Task UploadMultipartAsync(
        string localPath, string key, string contentType, CancellationToken ct)
    {
        using var transferUtility = new TransferUtility(_s3Client);

        var request = new TransferUtilityUploadRequest
        {
            BucketName = _settings.BucketName,
            Key = key,
            FilePath = localPath,
            ContentType = contentType,
            PartSize = 5 * 1024 * 1024, // 5MB parçalar
            DisablePayloadSigning = true,
        };

        request.UploadProgressEvent += (_, args) =>
        {
            if (args.PercentDone % 25 == 0)
            {
                _logger.LogInformation(
                    "Multipart upload: {Key} — %{Percent}",
                    key, args.PercentDone);
            }
        };

        await transferUtility.UploadAsync(request, ct);
    }
}
