using System.Collections.Concurrent;

namespace BookReader.Services;

public class LruCoverCacheService : ICoverCacheService
{
    private readonly ConcurrentDictionary<string, byte[]> _cache = new();
    private readonly Queue<string> _accessOrder = new();
    private readonly int _maxSize;
    private readonly string _cacheDir;

    public LruCoverCacheService(int maxSize = 100)
    {
        _maxSize = maxSize;
        _cacheDir = Path.Combine(FileSystem.AppDataDirectory, "cover_cache");
        Directory.CreateDirectory(_cacheDir);
    }

    public async Task<byte[]?> GetCoverAsync(string key)
    {
        // Try memory cache first
        if (_cache.TryGetValue(key, out var cached))
        {
            UpdateAccessOrder(key);
            return cached;
        }

        // Try disk cache
        var filePath = GetCacheFilePath(key);
        if (File.Exists(filePath))
        {
            var data = await File.ReadAllBytesAsync(filePath);
            await SetCoverAsync(key, data); // Add to memory cache
            return data;
        }

        return null;
    }

    public async Task SetCoverAsync(string key, byte[]? coverData)
    {
        if (coverData == null || coverData.Length == 0)
            return;

        // Add to memory cache
        if (_cache.Count >= _maxSize)
        {
            EvictOldest();
        }

        _cache.AddOrUpdate(key, coverData, (_, _) => coverData);
        UpdateAccessOrder(key);

        // Save to disk cache
        var filePath = GetCacheFilePath(key);
        await File.WriteAllBytesAsync(filePath, coverData);
    }

    public async Task ClearAsync()
    {
        _cache.Clear();
        _accessOrder.Clear();

        try
        {
            if (Directory.Exists(_cacheDir))
            {
                foreach (var file in Directory.GetFiles(_cacheDir))
                {
                    File.Delete(file);
                }
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine($"[CoverCache] Clear error: {ex.Message}");
        }

        await Task.CompletedTask;
    }

    private void UpdateAccessOrder(string key)
    {
        lock (_accessOrder)
        {
            if (_accessOrder.Contains(key))
            {
                var list = _accessOrder.ToList();
                list.Remove(key);
                _accessOrder.Clear();
                foreach (var item in list)
                    _accessOrder.Enqueue(item);
            }
            _accessOrder.Enqueue(key);
        }
    }

    private void EvictOldest()
    {
        lock (_accessOrder)
        {
            while (_accessOrder.Count > 0 && _cache.Count >= _maxSize)
            {
                var oldest = _accessOrder.Dequeue();
                if (_cache.TryRemove(oldest, out var data))
                {
                    var filePath = GetCacheFilePath(oldest);
                    if (File.Exists(filePath))
                    {
                        try { File.Delete(filePath); } catch { }
                    }
                }
            }
        }
    }

    private string GetCacheFilePath(string key)
    {
        // Sanitize key for filename
        var sanitized = string.Concat(key.Split(Path.GetInvalidFileNameChars()));
        return Path.Combine(_cacheDir, $"{sanitized}.cache");
    }
}
