using PlotLine.Models;
using SkiaSharp;

namespace PlotLine.Services;

public interface IFloorPlanBackgroundImageService
{
    Task<FloorPlanBackgroundImageUploadResult> UploadAsync(FloorPlan floorPlan, FloorPlanFloor floor, IFormFile upload);
    void DeleteBackgroundFile(string? backgroundImagePath);
}

public sealed record FloorPlanBackgroundImageUploadResult(
    bool Succeeded,
    string? StoragePath = null,
    string? OriginalFileName = null,
    string? ErrorMessage = null);

public sealed class FloorPlanBackgroundImageService(
    IUploadStorageService uploadStorage,
    ILogger<FloorPlanBackgroundImageService> logger) : IFloorPlanBackgroundImageService
{
    private const long MaxUploadBytes = 10L * 1024L * 1024L;
    private static readonly HashSet<string> SupportedExtensions = new(StringComparer.OrdinalIgnoreCase) { ".jpg", ".jpeg", ".png", ".webp" };

    public async Task<FloorPlanBackgroundImageUploadResult> UploadAsync(FloorPlan floorPlan, FloorPlanFloor floor, IFormFile upload)
    {
        if (upload.Length <= 0)
        {
            return new(false, ErrorMessage: "Choose a background image to upload.");
        }

        if (upload.Length > MaxUploadBytes)
        {
            return new(false, ErrorMessage: "Background image must be 10 MB or smaller.");
        }

        var extension = Path.GetExtension(upload.FileName);
        if (!SupportedExtensions.Contains(extension))
        {
            return new(false, ErrorMessage: "Background image must be a PNG, JPG, JPEG or WebP file.");
        }

        var originalFileName = Path.GetFileName(upload.FileName);
        if (string.IsNullOrWhiteSpace(originalFileName))
        {
            originalFileName = $"background{extension}";
        }

        byte[] bytes;
        await using (var stream = upload.OpenReadStream())
        using (var memory = new MemoryStream())
        {
            await stream.CopyToAsync(memory);
            bytes = memory.ToArray();
        }

        try
        {
            using var codec = SKCodec.Create(new MemoryStream(bytes)) ?? throw new InvalidDataException();
            if (codec.EncodedFormat is not (SKEncodedImageFormat.Jpeg or SKEncodedImageFormat.Png or SKEncodedImageFormat.Webp))
            {
                return new(false, ErrorMessage: "Background image must be a valid PNG, JPG, JPEG or WebP file.");
            }
        }
        catch (Exception ex) when (ex is InvalidDataException or ArgumentException)
        {
            return new(false, ErrorMessage: "Background image must be a valid PNG, JPG, JPEG or WebP file.");
        }

        var storedName = $"background-{Guid.NewGuid():N}{extension.ToLowerInvariant()}";
        var uploadRoot = uploadStorage.GetDirectory("floor-plans", floorPlan.ProjectID.ToString(), floorPlan.FloorPlanID.ToString(), "backgrounds");
        var absolutePath = Path.Combine(uploadRoot, storedName);
        var storagePath = uploadStorage.GetPublicPath("floor-plans", floorPlan.ProjectID.ToString(), floorPlan.FloorPlanID.ToString(), "backgrounds", storedName);

        try
        {
            await File.WriteAllBytesAsync(absolutePath, bytes);
            return new(true, storagePath, originalFileName);
        }
        catch (Exception ex)
        {
            logger.LogWarning(ex, "Floor plan background upload failed for floor {FloorPlanFloorID}.", floor.FloorPlanFloorID);
            return new(false, ErrorMessage: "Background image could not be saved. Try another image.");
        }
    }

    public void DeleteBackgroundFile(string? backgroundImagePath)
    {
        uploadStorage.TryDeleteFile(backgroundImagePath, "uploads/floor-plans");
    }
}
