using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.EntityFrameworkCore;
using DramaLing.Api.Data;
using DramaLing.Api.Models.Entities;
using System.Text.Json;
using System.Text;
using System.Security.Cryptography;

namespace DramaLing.Api.Services.Caching;

/// <summary>
/// 混合快取服務實作，支援記憶體快取和分散式快取的多層架構
/// </summary>
public class HybridCacheService : ICacheService
{
    private readonly IMemoryCache _memoryCache;
    private readonly IDistributedCache? _distributedCache;
    private readonly DramaLingDbContext _dbContext;
    private readonly ILogger<HybridCacheService> _logger;
    private readonly CacheStats _stats;
    private readonly JsonSerializerOptions _jsonOptions;

    public HybridCacheService(
        IMemoryCache memoryCache,
        DramaLingDbContext dbContext,
        ILogger<HybridCacheService> logger,
        IDistributedCache? distributedCache = null)
    {
        _memoryCache = memoryCache ?? throw new ArgumentNullException(nameof(memoryCache));
        _distributedCache = distributedCache;
        _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        _stats = new CacheStats { LastUpdated = DateTime.UtcNow };

        _jsonOptions = new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
            WriteIndented = false
        };

        _logger.LogInformation("HybridCacheService initialized with Memory Cache and {DistributedCache}",
            _distributedCache != null ? "Distributed Cache" : "No Distributed Cache");
    }

    #region 基本快取操作

    public async Task<T?> GetAsync<T>(string key) where T : class
    {
        if (string.IsNullOrEmpty(key))
            throw new ArgumentNullException(nameof(key));

        try
        {
            // L1: 記憶體快取 (最快)
            if (_memoryCache.TryGetValue(key, out T? memoryResult))
            {
                _stats.HitCount++;
                _logger.LogDebug("Cache hit from memory for key: {Key}", key);
                return memoryResult;
            }

            // L2: 分散式快取
            if (_distributedCache != null)
            {
                var distributedData = await _distributedCache.GetAsync(key);
                if (distributedData != null)
                {
                    var distributedResult = DeserializeFromBytes<T>(distributedData);
                    if (distributedResult != null)
                    {
                        // 回填到記憶體快取
                        var memoryExpiry = CalculateMemoryExpiry(key);
                        _memoryCache.Set(key, distributedResult, memoryExpiry);

                        _stats.HitCount++;
                        _logger.LogDebug("Cache hit from distributed cache for key: {Key}", key);
                        return distributedResult;
                    }
                }
            }

            // L3: 資料庫快取 (僅適用於分析結果)
            if (key.StartsWith("analysis:"))
            {
                var dbResult = await GetFromDatabaseCacheAsync<T>(key);
                if (dbResult != null)
                {
                    // 回填到上層快取
                    await SetMultiLevelCacheAsync(key, dbResult);

                    _stats.HitCount++;
                    _logger.LogDebug("Cache hit from database for key: {Key}", key);
                    return dbResult;
                }
            }

            _stats.MissCount++;
            _logger.LogDebug("Cache miss for key: {Key}", key);
            return null;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting cache for key: {Key}", key);
            _stats.MissCount++;
            return null;
        }
    }

    public async Task<bool> SetAsync<T>(string key, T value, TimeSpan? expiry = null) where T : class
    {
        if (string.IsNullOrEmpty(key))
            throw new ArgumentNullException(nameof(key));

        if (value == null)
            throw new ArgumentNullException(nameof(value));

        try
        {
            var smartExpiry = expiry ?? CalculateSmartExpiry(key, value);

            // 同時設定記憶體和分散式快取
            var tasks = new List<Task<bool>>();

            // L1: 記憶體快取
            tasks.Add(Task.Run(() =>
            {
                try
                {
                    var memoryExpiry = TimeSpan.FromMinutes(Math.Min(smartExpiry.TotalMinutes, 30)); // 記憶體快取最多30分鐘
                    _memoryCache.Set(key, value, memoryExpiry);
                    return true;
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Error setting memory cache for key: {Key}", key);
                    return false;
                }
            }));

            // L2: 分散式快取
            if (_distributedCache != null)
            {
                tasks.Add(SetDistributedCacheAsync(key, value, smartExpiry));
            }

            var results = await Task.WhenAll(tasks);
            var success = results.Any(r => r);

            if (success)
            {
                _stats.TotalKeys++;
                _logger.LogDebug("Cache set for key: {Key}, expiry: {Expiry}", key, smartExpiry);
            }

            return success;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error setting cache for key: {Key}", key);
            return false;
        }
    }

    public async Task<bool> RemoveAsync(string key)
    {
        if (string.IsNullOrEmpty(key))
            throw new ArgumentNullException(nameof(key));

        try
        {
            var tasks = new List<Task<bool>>();

            // 從記憶體快取移除
            tasks.Add(Task.Run(() =>
            {
                try
                {
                    _memoryCache.Remove(key);
                    return true;
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Error removing from memory cache for key: {Key}", key);
                    return false;
                }
            }));

            // 從分散式快取移除
            if (_distributedCache != null)
            {
                tasks.Add(Task.Run(async () =>
                {
                    try
                    {
                        await _distributedCache.RemoveAsync(key);
                        return true;
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "Error removing from distributed cache for key: {Key}", key);
                        return false;
                    }
                }));
            }

            var results = await Task.WhenAll(tasks);
            var success = results.Any(r => r);

            if (success)
            {
                _logger.LogDebug("Cache removed for key: {Key}", key);
            }

            return success;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error removing cache for key: {Key}", key);
            return false;
        }
    }

    public async Task<bool> ExistsAsync(string key)
    {
        if (string.IsNullOrEmpty(key))
            throw new ArgumentNullException(nameof(key));

        try
        {
            // 檢查記憶體快取
            if (_memoryCache.TryGetValue(key, out _))
            {
                return true;
            }

            // 檢查分散式快取
            if (_distributedCache != null)
            {
                var distributedData = await _distributedCache.GetAsync(key);
                return distributedData != null;
            }

            return false;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error checking cache existence for key: {Key}", key);
            return false;
        }
    }

    public async Task<bool> ExpireAsync(string key, TimeSpan expiry)
    {
        if (string.IsNullOrEmpty(key))
            throw new ArgumentNullException(nameof(key));

        try
        {
            // 重新設定過期時間（需要重新設定值）
            var value = await GetAsync<object>(key);
            if (value != null)
            {
                return await SetAsync(key, value, expiry);
            }

            return false;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error setting expiry for key: {Key}", key);
            return false;
        }
    }

    public async Task<bool> ClearAsync()
    {
        try
        {
            var tasks = new List<Task>();

            // 清除記憶體快取（如果支援）
            if (_memoryCache is MemoryCache memoryCache)
            {
                tasks.Add(Task.Run(() =>
                {
                    // MemoryCache 沒有直接清除所有項目的方法
                    // 這裡只能重新建立或等待自然過期
                    _logger.LogWarning("Memory cache clear is not directly supported");
                }));
            }

            // 分散式快取清除（取決於實作）
            if (_distributedCache != null)
            {
                tasks.Add(Task.Run(() =>
                {
                    _logger.LogWarning("Distributed cache clear implementation depends on the provider");
                }));
            }

            await Task.WhenAll(tasks);
            _logger.LogInformation("Cache clear operation completed");
            return true;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error clearing cache");
            return false;
        }
    }

    #endregion

    #region 批次操作

    public async Task<Dictionary<string, T?>> GetManyAsync<T>(IEnumerable<string> keys) where T : class
    {
        var keyList = keys.ToList();
        var result = new Dictionary<string, T?>();

        if (!keyList.Any())
            return result;

        try
        {
            var tasks = keyList.Select(async key =>
            {
                var value = await GetAsync<T>(key);
                return new KeyValuePair<string, T?>(key, value);
            });

            var results = await Task.WhenAll(tasks);
            return results.ToDictionary(r => r.Key, r => r.Value);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting multiple cache values");
            return result;
        }
    }

    public async Task<bool> SetManyAsync<T>(Dictionary<string, T> keyValuePairs, TimeSpan? expiry = null) where T : class
    {
        if (!keyValuePairs.Any())
            return true;

        try
        {
            var tasks = keyValuePairs.Select(async kvp =>
                await SetAsync(kvp.Key, kvp.Value, expiry));

            var results = await Task.WhenAll(tasks);
            return results.All(r => r);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error setting multiple cache values");
            return false;
        }
    }

    #endregion

    #region 統計資訊

    public Task<CacheStats> GetStatsAsync()
    {
        _stats.LastUpdated = DateTime.UtcNow;
        return Task.FromResult(_stats);
    }

    #endregion

    #region 私有方法

    private async Task<bool> SetDistributedCacheAsync<T>(string key, T value, TimeSpan expiry) where T : class
    {
        try
        {
            var serializedData = SerializeToBytes(value);
            var options = new DistributedCacheEntryOptions
            {
                SlidingExpiration = expiry
            };

            await _distributedCache!.SetAsync(key, serializedData, options);
            return true;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error setting distributed cache for key: {Key}", key);
            return false;
        }
    }

    private byte[] SerializeToBytes<T>(T value) where T : class
    {
        var json = JsonSerializer.Serialize(value, _jsonOptions);
        return Encoding.UTF8.GetBytes(json);
    }

    private T? DeserializeFromBytes<T>(byte[] data) where T : class
    {
        try
        {
            var json = Encoding.UTF8.GetString(data);
            return JsonSerializer.Deserialize<T>(json, _jsonOptions);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error deserializing cache data");
            return null;
        }
    }

    private TimeSpan CalculateSmartExpiry<T>(string key, T value)
    {
        // 根據不同的快取類型和鍵的特性計算智能過期時間
        return key switch
        {
            var k when k.StartsWith("analysis:") => TimeSpan.FromHours(2), // AI 分析結果快取2小時
            var k when k.StartsWith("user:") => TimeSpan.FromMinutes(30),   // 用戶資料快取30分鐘
            var k when k.StartsWith("flashcard:") => TimeSpan.FromMinutes(15), // 詞卡資料快取15分鐘
            var k when k.StartsWith("stats:") => TimeSpan.FromMinutes(5),   // 統計資料快取5分鐘
            _ => TimeSpan.FromMinutes(10) // 預設快取10分鐘
        };
    }

    private TimeSpan CalculateMemoryExpiry(string key)
    {
        // 記憶體快取時間通常比分散式快取短
        return key switch
        {
            var k when k.StartsWith("analysis:") => TimeSpan.FromMinutes(30),
            var k when k.StartsWith("user:") => TimeSpan.FromMinutes(10),
            var k when k.StartsWith("flashcard:") => TimeSpan.FromMinutes(5),
            var k when k.StartsWith("stats:") => TimeSpan.FromMinutes(2),
            _ => TimeSpan.FromMinutes(5)
        };
    }

    #region 資料庫快取 (L3)

    private async Task<T?> GetFromDatabaseCacheAsync<T>(string key) where T : class
    {
        try
        {
            if (!key.StartsWith("analysis:")) return null;

            var hash = key.Replace("analysis:", "");
            var cached = await _dbContext.SentenceAnalysisCache
                .AsNoTracking()
                .FirstOrDefaultAsync(c => c.InputTextHash == hash && c.ExpiresAt > DateTime.UtcNow);

            if (cached != null)
            {
                // 更新訪問統計
                cached.AccessCount++;
                cached.LastAccessedAt = DateTime.UtcNow;
                await _dbContext.SaveChangesAsync();

                var result = JsonSerializer.Deserialize<T>(cached.AnalysisResult, _jsonOptions);
                return result;
            }

            return null;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting from database cache for key: {Key}", key);
            return null;
        }
    }

    private async Task SaveToDatabaseCacheAsync<T>(string key, T value, TimeSpan expiry) where T : class
    {
        try
        {
            if (!key.StartsWith("analysis:")) return;

            var hash = key.Replace("analysis:", "");
            var expiresAt = DateTime.UtcNow.Add(expiry);

            var existing = await _dbContext.SentenceAnalysisCache
                .FirstOrDefaultAsync(c => c.InputTextHash == hash);

            if (existing != null)
            {
                existing.AnalysisResult = JsonSerializer.Serialize(value, _jsonOptions);
                existing.ExpiresAt = expiresAt;
                existing.AccessCount++;
                existing.LastAccessedAt = DateTime.UtcNow;
            }
            else
            {
                var cacheItem = new SentenceAnalysisCache
                {
                    Id = Guid.NewGuid(),
                    InputTextHash = hash,
                    InputText = "", // 需要從其他地方獲取原始文本
                    AnalysisResult = JsonSerializer.Serialize(value, _jsonOptions),
                    CreatedAt = DateTime.UtcNow,
                    ExpiresAt = expiresAt,
                    AccessCount = 1,
                    LastAccessedAt = DateTime.UtcNow
                };

                _dbContext.SentenceAnalysisCache.Add(cacheItem);
            }

            await _dbContext.SaveChangesAsync();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error saving to database cache for key: {Key}", key);
        }
    }

    private async Task SetMultiLevelCacheAsync<T>(string key, T value) where T : class
    {
        var expiry = CalculateSmartExpiry(key, value);

        // 設定記憶體快取
        var memoryExpiry = CalculateMemoryExpiry(key);
        _memoryCache.Set(key, value, memoryExpiry);

        // 設定分散式快取
        if (_distributedCache != null)
        {
            await SetDistributedCacheAsync(key, value, expiry);
        }
    }

    #endregion

    #endregion
}