using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
using Haoliang.Core.Services;
using Haoliang.Models.Device;
using Haoliang.Models.Production;
using Haoliang.Models.System;

namespace Haoliang.Core.Services
{
    public interface ICacheService
    {
        /// <summary>
        /// Get cached value or execute factory if not exists
        /// </summary>
        Task<T> GetOrSetAsync<T>(string key, Func<Task<T>> factory, MemoryCacheEntryOptions options = null);
        
        /// <summary>
        /// Get cached value synchronously
        /// </summary>
        T Get<T>(string key);
        
        /// <summary>
        /// Set cache value
        /// </summary>
        void Set<T>(string key, T value, MemoryCacheEntryOptions options = null);
        
        /// <summary>
        /// Remove cached value
        /// </summary>
        bool Remove(string key);
        
        /// <summary>
        /// Check if key exists in cache
        /// </summary>
        bool Exists(string key);
        
        /// <summary>
        /// Clear all cache
        /// </summary>
        void Clear();
        
        /// <summary>
        /// Get cache statistics
        /// </summary>
        CacheStatistics GetStatistics();
        
        /// <summary>
        /// Get cache keys matching pattern
        /// </summary>
        IEnumerable<string> GetKeys(string pattern);
        
        /// <summary>
        /// Refresh cached value
        /// </summary>
        bool Refresh<T>(string key);
    }

    public class CacheStatistics
    {
        public long TotalItems { get; set; }
        public long Hits { get; set; }
        public long Misses { get; set; }
        public double HitRate => Hits + Misses > 0 ? (double)Hits / (Hits + Misses) : 0;
        public long MemoryUsageBytes { get; set; }
        public DateTime LastCleared { get; set; }
        public Dictionary<string, long> ItemsByType { get; set; }
    }

    public class MemoryCacheService : ICacheService
    {
        private readonly IMemoryCache _memoryCache;
        private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
        private long _hits = 0;
        private long _misses = 0;
        private long _memoryUsage = 0;

        public MemoryCacheService(IMemoryCache memoryCache)
        {
            _memoryCache = memoryCache;
        }

        public Task<T> GetOrSetAsync<T>(string key, Func<Task<T>> factory, MemoryCacheEntryOptions options = null)
        {
            return Task.Run(async () =>
            {
                // Try to get from cache first
                if (_memoryCache.TryGetValue(key, out T value))
                {
                    Interlocked.Increment(ref _hits);
                    return value;
                }

                Interlocked.Increment(ref _misses);

                // Create new value
                value = await factory();

                if (value != null)
                {
                    // Set cache options if not provided
                    options = options ?? new MemoryCacheEntryOptions
                    {
                        SlidingExpiration = TimeSpan.FromMinutes(30),
                        AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2),
                        Size = 1024 // 1KB
                    };

                    // Set cache with lock
                    _lock.EnterWriteLock();
                    try
                    {
                        _memoryCache.Set(key, value, options);
                        UpdateMemoryUsage(options.Size.GetValueOrDefault(1024));
                    }
                    finally
                    {
                        _lock.ExitWriteLock();
                    }
                }

                return value;
            });
        }

        public T Get<T>(string key)
        {
            if (_memoryCache.TryGetValue(key, out T value))
            {
                Interlocked.Increment(ref _hits);
                return value;
            }

            Interlocked.Increment(ref _misses);
            return default(T);
        }

        public void Set<T>(string key, T value, MemoryCacheEntryOptions options = null)
        {
            if (value == null)
                return;

            options = options ?? new MemoryCacheEntryOptions
            {
                SlidingExpiration = TimeSpan.FromMinutes(30),
                AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2),
                Size = 1024 // 1KB
            };

            _lock.EnterWriteLock();
            try
            {
                _memoryCache.Set(key, value, options);
                UpdateMemoryUsage(options.Size.GetValueOrDefault(1024));
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        }

        public bool Remove(string key)
        {
            _lock.EnterWriteLock();
            try
            {
                if (_memoryCache.TryGetValue(key, out object value))
                {
                    _memoryCache.Remove(key);
                    UpdateMemoryUsage(-(GetEstimatedSize(value)));
                    return true;
                }
                return false;
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        }

        public bool Exists(string key)
        {
            return _memoryCache.TryGetValue(key, out _);
        }

        public void Clear()
        {
            _lock.EnterWriteLock();
            try
            {
                // This is a simplified implementation
                // In a real scenario, you might need a more sophisticated way to clear the cache
                _memoryCache.Compact(1.0); // Remove all entries
                _memoryUsage = 0;
                Interlocked.Exchange(ref _hits, 0);
                Interlocked.Exchange(ref _misses, 0);
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        }

        public CacheStatistics GetStatistics()
        {
            _lock.EnterReadLock();
            try
            {
                var stats = new CacheStatistics
                {
                    TotalItems = GetCacheSize(),
                    Hits = _hits,
                    Misses = _misses,
                    MemoryUsageBytes = _memoryUsage,
                    LastCleared = DateTime.Now,
                    ItemsByType = new Dictionary<string, long>()
                };

                // Count items by type (simplified)
                var deviceCacheCount = GetKeys("device:*").Count();
                var productionCacheCount = GetKeys("production:*").Count();
                var configCacheCount = GetKeys("config:*").Count();
                var templateCacheCount = GetKeys("template:*").Count();

                stats.ItemsByType["device"] = deviceCacheCount;
                stats.ItemsByType["production"] = productionCacheCount;
                stats.ItemsByType["config"] = configCacheCount;
                stats.ItemsByType["template"] = templateCacheCount;

                return stats;
            }
            finally
            {
                _lock.ExitReadLock();
            }
        }

        public IEnumerable<string> GetKeys(string pattern)
        {
            // This is a simplified implementation
            // In a real scenario, you might need a more sophisticated key pattern matching
            return _memoryCache.Keys
                .Cast<string>()
                .Where(key => key.StartsWith(pattern.Replace("*", "")));
        }

        public bool Refresh<T>(string key)
        {
            if (!_memoryCache.TryGetValue(key, out T value))
                return false;

            // Remove and re-add to refresh
            _lock.EnterWriteLock();
            try
            {
                _memoryCache.Remove(key);
                _memoryCache.Set(key, value, new MemoryCacheEntryOptions
                {
                    SlidingExpiration = TimeSpan.FromMinutes(30),
                    AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2)
                });
                return true;
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        }

        #region Private Methods

        private int GetCacheSize()
        {
            // This is an approximation
            return _memoryCache.Count;
        }

        private void UpdateMemoryUsage(long delta)
        {
            Interlocked.Add(ref _memoryUsage, delta);
        }

        private int GetEstimatedSize(object obj)
        {
            // Simplified size estimation
            if (obj == null) return 0;
            
            var type = obj.GetType();
            if (type.IsValueType || type == typeof(string))
            {
                return System.Text.Json.JsonSerializer.Serialize(obj).Length;
            }
            
            // For complex objects, estimate based on type
            return 1024; // Default 1KB for complex objects
        }

        #endregion
    }

    // Extension methods for common caching patterns
    public static class CacheServiceExtensions
    {
        /// <summary>
        /// Cache device information
        /// </summary>
        public static Task<CNCDevice> GetOrSetDeviceAsync(this ICacheService cache, int deviceId, 
            Func<Task<CNCDevice>> factory, TimeSpan? expiration = null)
        {
            var options = new MemoryCacheEntryOptions();
            if (expiration.HasValue)
            {
                options.SlidingExpiration = expiration.Value;
                options.AbsoluteExpirationRelativeToNow = expiration.Value + TimeSpan.FromMinutes(30);
            }
            else
            {
                options.SlidingExpiration = TimeSpan.FromMinutes(30);
                options.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2);
            }

            return cache.GetOrSetAsync($"device:{deviceId}", factory, options);
        }

        /// <summary>
        /// Cache device list
        /// </summary>
        public static Task<List<CNCDevice>> GetOrSetAllDevicesAsync(this ICacheService cache, 
            Func<Task<List<CNCDevice>>> factory, TimeSpan? expiration = null)
        {
            var options = new MemoryCacheEntryOptions
            {
                SlidingExpiration = expiration ?? TimeSpan.FromMinutes(15),
                AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1)
            };

            return cache.GetOrSetAsync("devices:all", factory, options);
        }

        /// <summary>
        /// Cache device status
        /// </summary>
        public static Task<DeviceCurrentStatus> GetOrSetDeviceStatusAsync(this ICacheService cache, int deviceId, 
            Func<Task<DeviceCurrentStatus>> factory, TimeSpan? expiration = null)
        {
            var options = new MemoryCacheEntryOptions
            {
                SlidingExpiration = expiration ?? TimeSpan.FromMinutes(5),
                AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10)
            };

            return cache.GetOrSetAsync($"device:status:{deviceId}", factory, options);
        }

        /// <summary>
        /// Cache production records
        /// </summary>
        public static Task<List<ProductionRecord>> GetOrSetProductionRecordsAsync(this ICacheService cache, 
            int deviceId, DateTime date, Func<Task<List<ProductionRecord>>> factory, TimeSpan? expiration = null)
        {
            var options = new MemoryCacheEntryOptions
            {
                SlidingExpiration = expiration ?? TimeSpan.FromMinutes(10),
                AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1)
            };

            return cache.GetOrSetAsync($"production:records:{deviceId}:{date:yyyy-MM-dd}", factory, options);
        }

        /// <summary>
        /// Cache production summary
        /// </summary>
        public static Task<ProgramProductionSummary> GetOrSetProductionSummaryAsync(this ICacheService cache, 
            int deviceId, string programName, Func<Task<ProgramProductionSummary>> factory, TimeSpan? expiration = null)
        {
            var options = new MemoryCacheEntryOptions
            {
                SlidingExpiration = expiration ?? TimeSpan.FromMinutes(30),
                AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2)
            };

            return cache.GetOrSetAsync($"production:summary:{deviceId}:{programName}", factory, options);
        }

        /// <summary>
        /// Cache system configuration
        /// </summary>
        public static Task<SystemConfiguration> GetOrSetSystemConfigurationAsync(this ICacheService cache, 
            Func<Task<SystemConfiguration>> factory, TimeSpan? expiration = null)
        {
            var options = new MemoryCacheEntryOptions
            {
                SlidingExpiration = expiration ?? TimeSpan.FromHours(1),
                AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(6)
            };

            return cache.GetOrSetAsync("config:system", factory, options);
        }

        /// <summary>
        /// Cache template
        /// </summary>
        public static Task<CNCBrandTemplate> GetOrSetTemplateAsync(this ICacheService cache, 
            int templateId, Func<Task<CNCBrandTemplate>> factory, TimeSpan? expiration = null)
        {
            var options = new MemoryCacheEntryOptions
            {
                SlidingExpiration = expiration ?? TimeSpan.FromMinutes(30),
                AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2)
            };

            return cache.GetOrSetAsync($"template:{templateId}", factory, options);
        }

        /// <summary>
        /// Cache template list
        /// </summary>
        public static Task<List<CNCBrandTemplate>> GetOrSetAllTemplatesAsync(this ICacheService cache, 
            Func<Task<List<CNCBrandTemplate>>> factory, TimeSpan? expiration = null)
        {
            var options = new MemoryCacheEntryOptions
            {
                SlidingExpiration = expiration ?? TimeSpan.FromMinutes(30),
                AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2)
            };

            return cache.GetOrSetAsync("templates:all", factory, options);
        }

        /// <summary>
        /// Cache alert configuration
        /// </summary>
        public static Task<AlertConfiguration> GetOrSetAlertConfigurationAsync(this ICacheService cache, 
            Func<Task<AlertConfiguration>> factory, TimeSpan? expiration = null)
        {
            var options = new MemoryCacheEntryOptions
            {
                SlidingExpiration = expiration ?? TimeSpan.FromMinutes(30),
                AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1)
            };

            return cache.GetOrSetAsync("config:alerts", factory, options);
        }

        /// <summary>
        /// Cache dashboard summary
        /// </summary>
        public static Task<DashboardSummary> GetOrSetDashboardSummaryAsync(this ICacheService cache, 
            DateTime date, Func<Task<DashboardSummary>> factory, TimeSpan? expiration = null)
        {
            var options = new MemoryCacheEntryOptions
            {
                SlidingExpiration = expiration ?? TimeSpan.FromMinutes(10),
                AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30)
            };

            return cache.GetOrSetAsync($"dashboard:summary:{date:yyyy-MM-dd}", factory, options);
        }

        /// <summary>
        /// Invalidate device-related cache
        /// </summary>
        public static void InvalidateDeviceCache(this ICacheService cache, int deviceId, params string[] additionalKeys)
        {
            var keys = new[]
            {
                $"device:{deviceId}",
                $"device:status:{deviceId}",
                $"production:records:{deviceId}",
                $"production:summary:{deviceId}"
            };

            foreach (var key in keys.Concat(additionalKeys))
            {
                cache.Remove(key);
            }
        }

        /// <summary>
        /// Invalidate production-related cache
        /// </summary>
        public static void InvalidateProductionCache(this ICacheService cache, int deviceId, string programName, DateTime date)
        {
            cache.Remove($"production:records:{deviceId}:{date:yyyy-MM-dd}");
            cache.Remove($"production:summary:{deviceId}:{programName}");
        }

        /// <summary>
        /// Invalidate template-related cache
        /// </summary>
        public static void InvalidateTemplateCache(this ICacheService cache, int templateId)
        {
            cache.Remove($"template:{templateId}");
            cache.Remove("templates:all");
        }

        /// <summary>
        /// Invalidate system configuration cache
        /// </summary>
        public static void InvalidateSystemConfigCache(this ICacheService cache)
        {
            cache.Remove("config:system");
            cache.Remove("config:alerts");
            cache.Remove("dashboard:summary");
        }

        /// <summary>
        /// Invalidate dashboard cache
        /// </summary>
        public static void InvalidateDashboardCache(this ICacheService cache, DateTime date)
        {
            cache.Remove($"dashboard:summary:{date:yyyy-MM-dd}");
        }

        /// <summary>
        /// Get or set with sliding expiration for frequently accessed data
        /// </summary>
        public static Task<T> GetOrSetWithSlidingExpiration<T>(this ICacheService cache, string key, 
            Func<Task<T>> factory, TimeSpan slidingExpiration)
        {
            var options = new MemoryCacheEntryOptions
            {
                SlidingExpiration = slidingExpiration
            };

            return cache.GetOrSetAsync(key, factory, options);
        }

        /// <summary>
        /// Get or set with absolute expiration for time-sensitive data
        /// </summary>
        public static Task<T> GetOrSetWithAbsoluteExpiration<T>(this ICacheService cache, string key, 
            Func<Task<T>> factory, TimeSpan absoluteExpiration)
        {
            var options = new MemoryCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = absoluteExpiration
            };

            return cache.GetOrSetAsync(key, factory, options);
        }

        /// <summary>
        /// Get or set with priority for important data
        /// </summary>
        public static Task<T> GetOrSetWithPriority<T>(this ICacheService cache, string key, 
            Func<Task<T>> factory, CacheItemPriority priority)
        {
            var options = new MemoryCacheEntryOptions
            {
                Priority = priority
            };

            return cache.GetOrSetAsync(key, factory, options);
        }
    }

    /// <summary>
    /// Cache service for distributed caching
    /// </summary>
    public interface IDistributedCacheService : ICacheService
    {
        /// <summary>
        /// Get distributed lock
        /// </summary>
        Task<IDistributedLock> AcquireLockAsync(string key, TimeSpan timeout);

        /// <summary>
        /// Refresh distributed cache
        /// </summary>
        Task RefreshAsync(string key);

        /// <summary>
        /// Get distributed cache statistics
        /// </summary>
        Task<DistributedCacheStatistics> GetDistributedStatisticsAsync();
    }

    public interface IDistributedLock : IDisposable
    {
        bool IsAcquired { get; }
        void Release();
    }

    public class DistributedCacheStatistics : CacheStatistics
    {
        public int ConnectedNodes { get; set; }
        public long NetworkCalls { get; set; }
        public long SynchronizationErrors { get; set; }
    }

    /// <summary>
    /// Cache manager for managing multiple cache instances
    /// </summary>
    public interface ICacheManager
    {
        ICacheService LocalCache { get; }
        IDistributedCacheService DistributedCache { get; }
        void ConfigureCacheSettings(CacheSettings settings);
        void InitializeCaches();
    }

    public class CacheSettings
    {
        public bool EnableLocalCache { get; set; } = true;
        public bool EnableDistributedCache { get; set; } = false;
        public TimeSpan DefaultSlidingExpiration { get; set; } = TimeSpan.FromMinutes(30);
        public TimeSpan DefaultAbsoluteExpiration { get; set; } = TimeSpan.FromHours(2);
        public long MaxMemorySizeBytes { get; set; } = 1024 * 1024 * 100; // 100MB
        public CacheEvictionPolicy EvictionPolicy { get; set; } = CacheEvictionPolicy.LRU;
        public bool EnableCacheLogging { get; set; } = false;
        public TimeSpan CacheRefreshInterval { get; set; } = TimeSpan.FromMinutes(5);
    }

    public enum CacheEvictionPolicy
    {
        LRU, // Least Recently Used
        LFU, // Least Frequently Used
        FIFO, // First In First Out
        Random
    }

    public class CacheManager : ICacheManager
    {
        public ICacheService LocalCache { get; private set; }
        public IDistributedCacheService DistributedCache { get; private set; }

        public CacheManager(ICacheService localCache, IDistributedCacheService distributedCache)
        {
            LocalCache = localCache;
            DistributedCache = distributedCache;
        }

        public void ConfigureCacheSettings(CacheSettings settings)
        {
            // Configure cache settings based on provided configuration
            // This would involve setting up the cache instances with the specified settings
        }

        public void InitializeCaches()
        {
            // Initialize caches with default settings
            ClearAllCaches();
        }

        public void ClearAllCaches()
        {
            LocalCache?.Clear();
            DistributedCache?.Clear();
        }
    }
}