using System.IO;
using System.Net.Http;
using CMSMicroservice.Application.Common.FileManager;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Processing;

namespace CMSMicroservice.Infrastructure.Services;

/// <summary>
/// فایل‌منیجر محلی — فایل‌ها روی دیسک ذخیره می‌شوند
/// مسیر نسبی در دیتابیس ذخیره می‌شود
/// موقع واکشی: فایل از دیسک خوانده و به base64 data-URI تبدیل می‌شود
/// </summary>
public sealed class LocalFileManager : IFileManager
{
    private readonly ILogger<LocalFileManager> _logger;
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly string _uploadRoot;
    private readonly string _fmsBaseUrl;

    // ── تنظیمات بهینه‌سازی تصویر ──
    private const int MainMaxWidth = 1200;
    private const int MainMaxHeight = 1200;
    private const int ThumbMaxWidth = 300;
    private const int ThumbMaxHeight = 300;
    private const int JpegQuality = 75;

    public LocalFileManager(IConfiguration configuration, IHttpClientFactory httpClientFactory, ILogger<LocalFileManager> logger)
    {
        _logger = logger;
        _httpClientFactory = httpClientFactory;

        // مسیر ذخیره فایل‌ها — پیش‌فرض: پوشه Uploads در کنار WebApi
        _uploadRoot = configuration["FileStorage:UploadPath"]
                      ?? Path.Combine(AppContext.BaseDirectory, "Uploads");

        _fmsBaseUrl = configuration["FMS:Address"]?.TrimEnd('/') ?? "https://dl.afrino.co";

        Directory.CreateDirectory(_uploadRoot);
        _logger.LogInformation("LocalFileManager initialized — UploadRoot: {Root}", _uploadRoot);
    }

    // ────────────────────────────────────────────────────
    //  آپلود فایل خام → ذخیره روی دیسک → برگرداندن مسیر نسبی
    // ────────────────────────────────────────────────────
    public async Task<UploadedFile> UploadAsync(
        string directory, byte[] fileBytes, string mime,
        string? fileName = null, CancellationToken ct = default)
    {
        if (fileBytes is not { Length: > 0 })
            throw new FileUploadException("فایلی برای آپلود ارسال نشده است");

        try
        {
            var ext = GetExtension(mime, fileName);
            var uniqueName = $"{Guid.NewGuid():N}{ext}";
            var relativePath = Path.Combine(directory, uniqueName).Replace('\\', '/');

            var fullPath = Path.Combine(_uploadRoot, relativePath);
            Directory.CreateDirectory(Path.GetDirectoryName(fullPath)!);

            await File.WriteAllBytesAsync(fullPath, fileBytes, ct);

            _logger.LogInformation(
                "File saved — Path: {Path}, Size: {Size}KB",
                relativePath, fileBytes.Length / 1024);

            return new UploadedFile(0, relativePath);
        }
        catch (FileUploadException) { throw; }
        catch (Exception ex)
        {
            _logger.LogError(ex, "خطا در ذخیره فایل — Directory: {Dir}", directory);
            throw new FileUploadException($"خطا در ذخیره فایل: {ex.Message}", ex);
        }
    }

    // ────────────────────────────────────────────────────
    //  آپلود تصویر + بندانگشتی → ذخیره روی دیسک
    // ────────────────────────────────────────────────────
    public async Task<UploadedImage> UploadImageAsync(
        string directory, byte[] fileBytes, string mime,
        string? fileName = null, CancellationToken ct = default)
    {
        if (fileBytes is not { Length: > 0 })
            throw new FileUploadException("تصویری برای آپلود ارسال نشده است");

        var baseName = Guid.NewGuid().ToString("N");

        // ① بهینه‌سازی و ذخیره تصویر اصلی
        var mainBytes = await OptimizeAsync(fileBytes, MainMaxWidth, MainMaxHeight);
        var mainRelative = Path.Combine(directory, $"{baseName}.jpg").Replace('\\', '/');
        var mainFull = Path.Combine(_uploadRoot, mainRelative);
        Directory.CreateDirectory(Path.GetDirectoryName(mainFull)!);
        await File.WriteAllBytesAsync(mainFull, mainBytes, ct);
        var main = new UploadedFile(0, mainRelative);

        // ② ساخت و ذخیره بندانگشتی
        var thumbBytes = await OptimizeAsync(fileBytes, ThumbMaxWidth, ThumbMaxHeight);
        var thumbRelative = Path.Combine(directory, $"{baseName}_thumb.jpg").Replace('\\', '/');
        var thumbFull = Path.Combine(_uploadRoot, thumbRelative);
        await File.WriteAllBytesAsync(thumbFull, thumbBytes, ct);
        var thumb = new UploadedFile(0, thumbRelative);

        _logger.LogInformation(
            "Image saved — Main: {MainPath} ({MainKB}KB), Thumb: {ThumbPath} ({ThumbKB}KB)",
            mainRelative, mainBytes.Length / 1024,
            thumbRelative, thumbBytes.Length / 1024);

        return new UploadedImage(main, thumb);
    }

    // ────────────────────────────────────────────────────
    //  حذف فایل از دیسک
    // ────────────────────────────────────────────────────
    public Task DeleteAsync(long fileId, CancellationToken ct = default)
    {
        _logger.LogWarning("DeleteAsync called with fileId={Id} — file deletion by ID not supported in disk mode", fileId);
        return Task.CompletedTask;
    }

    // ────────────────────────────────────────────────────
    //  خواندن فایل از دیسک → تبدیل به base64 data-URI
    // ────────────────────────────────────────────────────
    public string ResolveImageUrl(string? path)
    {
        if (string.IsNullOrWhiteSpace(path))
            return string.Empty;

        // اگر از قبل data-URI یا URL مطلق هست، همان را برگردان
        if (path.StartsWith("data:", StringComparison.OrdinalIgnoreCase) ||
            path.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
            path.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
            return path;

        try
        {
            var fullPath = Path.Combine(_uploadRoot, path.TrimStart('/'));
            if (!File.Exists(fullPath))
            {
                _logger.LogWarning("Image file not found on disk, trying FMS fallback: {Path}", fullPath);

                // ── FMS Fallback: دانلود از dl.afrino.co و کش محلی (برای مهاجرت) ──
                if (!TryDownloadFromFms(path.TrimStart('/'), fullPath))
                    return string.Empty;

                _logger.LogInformation("Downloaded and cached from FMS: {Path}", path);
            }

            var bytes = File.ReadAllBytes(fullPath);
            var mime = GetMimeFromExtension(Path.GetExtension(fullPath));
            return $"data:{mime};base64,{Convert.ToBase64String(bytes)}";
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error reading image from disk: {Path}", path);
            return string.Empty;
        }
    }

    // ────────────────────────────────────────────────────
    //  FMS Fallback — دانلود از سرور قدیمی و کش محلی (مهاجرت)
    // ────────────────────────────────────────────────────
    private bool TryDownloadFromFms(string relativePath, string localPath)
    {
        try
        {
            var fmsUrl = $"{_fmsBaseUrl}/{relativePath}";
            _logger.LogInformation("Attempting FMS download: {Url}", fmsUrl);

            using var client = _httpClientFactory.CreateClient("FMS");
            using var response = client.Send(new HttpRequestMessage(HttpMethod.Get, fmsUrl),
                HttpCompletionOption.ResponseHeadersRead);

            if (!response.IsSuccessStatusCode)
            {
                _logger.LogWarning("FMS returned {Status} for {Url}", response.StatusCode, fmsUrl);
                return false;
            }

            // ذخیره روی دیسک
            var directory = Path.GetDirectoryName(localPath)!;
            Directory.CreateDirectory(directory);

            using var responseStream = response.Content.ReadAsStream();
            using var fileStream = new FileStream(localPath, FileMode.Create, FileAccess.Write, FileShare.None);
            responseStream.CopyTo(fileStream);

            _logger.LogInformation("Cached FMS file locally: {Path} ({Size} bytes)", relativePath, fileStream.Length);
            return true;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to download from FMS: {Path}", relativePath);
            return false;
        }
    }

    // ────────────────────────────────────────────────────
    //  بهینه‌سازی تصویر (ریسایز + فشرده‌سازی JPEG)
    // ────────────────────────────────────────────────────
    private static async Task<byte[]> OptimizeAsync(byte[] imageBytes, int maxWidth, int maxHeight)
    {
        using var image = SixLabors.ImageSharp.Image.Load(imageBytes);

        if (image.Width > maxWidth || image.Height > maxHeight)
        {
            image.Mutate(x => x.Resize(new ResizeOptions
            {
                Size = new Size(maxWidth, maxHeight),
                Mode = ResizeMode.Max
            }));
        }

        using var ms = new MemoryStream();
        await image.SaveAsJpegAsync(ms, new JpegEncoder { Quality = JpegQuality });
        return ms.ToArray();
    }

    // ────────────────────────────────────────────────────
    //  پسوند فایل از mime type
    // ────────────────────────────────────────────────────
    private static string GetExtension(string mime, string? fileName)
    {
        if (!string.IsNullOrEmpty(fileName))
        {
            var ext = Path.GetExtension(fileName);
            if (!string.IsNullOrEmpty(ext))
                return ext.ToLowerInvariant();
        }

        return mime.ToLowerInvariant() switch
        {
            "image/jpeg" or "image/jpg" => ".jpg",
            "image/png" => ".png",
            "image/gif" => ".gif",
            "image/webp" => ".webp",
            "image/svg+xml" => ".svg",
            "application/pdf" => ".pdf",
            _ => ".bin"
        };
    }

    private static string GetMimeFromExtension(string extension)
    {
        return extension.ToLowerInvariant() switch
        {
            ".jpg" or ".jpeg" => "image/jpeg",
            ".png" => "image/png",
            ".gif" => "image/gif",
            ".webp" => "image/webp",
            ".svg" => "image/svg+xml",
            ".pdf" => "application/pdf",
            _ => "application/octet-stream"
        };
    }
}
