using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Media.Imaging;

namespace NexusShell.Services;

public sealed class NexusThumbnailService
{
    private readonly string _cacheRoot;

    public NexusThumbnailService()
    {
        _cacheRoot = Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
            "NinjaNexus",
            "photos",
            "thumbnails");
        Directory.CreateDirectory(_cacheRoot);
    }

    public int ClampSize(int requestedSize) =>
        Math.Clamp(requestedSize, 140, 220);

    public int SizeForZoomLevel(int zoomLevel) => zoomLevel switch
    {
        0 => 140,
        1 => 180,
        _ => 220,
    };

    public BitmapImage? GetThumbnail(string filePath, int size)
    {
        var clamped = ClampSize(size);
        if (!File.Exists(filePath))
        {
            return null;
        }

        try
        {
            var cachePath = GetCachePath(filePath, clamped);
            if (File.Exists(cachePath))
            {
                return LoadBitmap(cachePath, clamped, fromCache: true);
            }

            var generated = GenerateThumbnail(filePath, clamped);
            if (generated is null)
            {
                return null;
            }

            SaveToCache(generated, cachePath, clamped);
            return LoadBitmap(cachePath, clamped, fromCache: true);
        }
        catch
        {
            return TryDirectLoad(filePath, clamped);
        }
    }

    private static BitmapImage? TryDirectLoad(string filePath, int size)
    {
        try
        {
            return LoadBitmap(filePath, size, fromCache: false);
        }
        catch
        {
            return null;
        }
    }

    private static BitmapImage LoadBitmap(string path, int size, bool fromCache)
    {
        var image = new BitmapImage();
        image.BeginInit();
        image.CacheOption = BitmapCacheOption.OnLoad;
        image.UriSource = new Uri(path, UriKind.Absolute);
        image.DecodePixelWidth = size;
        image.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
        image.EndInit();
        image.Freeze();
        return image;
    }

    private static BitmapImage? GenerateThumbnail(string filePath, int size)
    {
        using var stream = File.OpenRead(filePath);
        var decoder = BitmapDecoder.Create(
            stream,
            BitmapCreateOptions.IgnoreColorProfile,
            BitmapCacheOption.OnLoad);
        var frame = decoder.Frames.FirstOrDefault();
        if (frame is null)
        {
            return null;
        }

        var scale = size / (double)Math.Max(frame.PixelWidth, frame.PixelHeight);
        var targetWidth = Math.Max(1, (int)(frame.PixelWidth * scale));
        var transformed = new TransformedBitmap(frame, new System.Windows.Media.ScaleTransform(scale, scale));
        var encoder = new JpegBitmapEncoder { QualityLevel = 82 };
        encoder.Frames.Add(BitmapFrame.Create(transformed));

        using var output = new MemoryStream();
        encoder.Save(output);
        output.Position = 0;

        var image = new BitmapImage();
        image.BeginInit();
        image.CacheOption = BitmapCacheOption.OnLoad;
        image.StreamSource = output;
        image.DecodePixelWidth = targetWidth;
        image.EndInit();
        image.Freeze();
        return image;
    }

    private static void SaveToCache(BitmapImage source, string cachePath, int size)
    {
        try
        {
            var encoder = new JpegBitmapEncoder { QualityLevel = 82 };
            encoder.Frames.Add(BitmapFrame.Create(source));
            using var stream = File.Create(cachePath);
            encoder.Save(stream);
        }
        catch
        {
            // Cache miss is acceptable; direct load still works.
        }
    }

    private string GetCachePath(string filePath, int size)
    {
        var info = new FileInfo(filePath);
        var key = $"{filePath}|{info.Length}|{info.LastWriteTimeUtc.Ticks}|{size}";
        var hash = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(key)))[..24];
        return Path.Combine(_cacheRoot, $"{hash}.jpg");
    }
}
