using System;
using System.Collections.Generic;
using System.Runtime.Caching;
using System.Web;

namespace CNCSystem.Core.Cache
{
    public class MemoryCacheService : ICacheService
    {
        private readonly ObjectCache _cache;
        private readonly TimeSpan _defaultExpiration;

        public MemoryCacheService()
        {
            _cache = MemoryCache.Default;
            _defaultExpiration = TimeSpan.FromMinutes(30);
        }

        public T Get<T>(string key)
        {
            if (string.IsNullOrWhiteSpace(key))
                throw new ArgumentException("缓存键不能为空", nameof(key));

            var value = _cache.Get(key);
            return value != null ? (T)value : default(T);
        }

        public void Set<T>(string key, T value, TimeSpan? expiration = null)
        {
            if (string.IsNullOrWhiteSpace(key))
                throw new ArgumentException("缓存键不能为空", nameof(key));

            if (value == null)
                throw new ArgumentException("缓存值不能为null", nameof(value));

            var policy = new CacheItemPolicy
            {
                AbsoluteExpiration = expiration ?? _defaultExpiration
            };

            _cache.Set(key, value, policy);
        }

        public void Remove(string key)
        {
            if (string.IsNullOrWhiteSpace(key))
                throw new ArgumentException("缓存键不能为空", nameof(key));

            _cache.Remove(key);
        }

        public void Clear()
        {
            var cacheKeys = _cache.Select(item => item.Key).ToList();
            foreach (var key in cacheKeys)
            {
                _cache.Remove(key);
            }
        }

        public bool Exists(string key)
        {
            if (string.IsNullOrWhiteSpace(key))
                return false;

            return _cache.Contains(key);
        }

        public T GetOrSet<T>(string key, Func<T> factory, TimeSpan? expiration = null)
        {
            if (string.IsNullOrWhiteSpace(key))
                throw new ArgumentException("缓存键不能为空", nameof(key));

            var value = Get<T>(key);
            if (value != null)
                return value;

            lock (key)
            {
                value = Get<T>(key);
                if (value == null)
                {
                    value = factory();
                    Set(key, value, expiration);
                }
            }

            return value;
        }
    }

    public interface ICacheService
    {
        T Get<T>(string key);
        void Set<T>(string key, T value, TimeSpan? expiration = null);
        void Remove(string key);
        void Clear();
        bool Exists(string key);
        T GetOrSet<T>(string key, Func<T> factory, TimeSpan? expiration = null);
    }

    public class CacheKeys
    {
        public static string DeviceStatus => "DeviceStatus";
        public static string ProductionData => "ProductionData";
        public static string Alarms => "Alarms";
        public static string SystemConfig => "SystemConfig";
        public static string Templates => "Templates";
        public static string Users => "Users";
        public static string Roles => "Roles";

        public static string DeviceStatusKey(int deviceId) => $"{DeviceStatus}_{deviceId}";
        public static string ProductionDataKey(DateTime date) => $"{ProductionData}_{date:yyyy-MM-dd}";
        public static string AlarmsKey => Alarms;
        public static string SystemConfigKey(string configKey) => $"{SystemConfig}_{configKey}";
        public static string TemplatesKey => Templates;
        public static string UsersKey => Users;
        public static string RolesKey => Roles;
    }

    public static class CacheExtensions
    {
        public static T GetFromCache<T>(this ICacheService cache, string key)
        {
            return cache.Get<T>(key);
        }

        public static void SetToCache<T>(this ICacheService cache, string key, T value, TimeSpan? expiration = null)
        {
            cache.Set(key, value, expiration);
        }

        public static T GetOrSetFromCache<T>(this ICacheService cache, string key, Func<T> factory, TimeSpan? expiration = null)
        {
            return cache.GetOrSet(key, factory, expiration);
        }

        public static void RemoveFromCache(this ICacheService cache, string key)
        {
            cache.Remove(key);
        }

        public static bool ExistsInCache(this ICacheService cache, string key)
        {
            return cache.Exists(key);
        }
    }

    public class CacheHelper
    {
        private static readonly ICacheService _cache = new MemoryCacheService();

        public static T Get<T>(string key)
        {
            return _cache.Get<T>(key);
        }

        public static void Set<T>(string key, T value, TimeSpan? expiration = null)
        {
            _cache.Set(key, value, expiration);
        }

        public static T GetOrSet<T>(string key, Func<T> factory, TimeSpan? expiration = null)
        {
            return _cache.GetOrSet(key, factory, expiration);
        }

        public static void Remove(string key)
        {
            _cache.Remove(key);
        }

        public static void Clear()
        {
            _cache.Clear();
        }

        public static bool Exists(string key)
        {
            return _cache.Exists(key);
        }
    }
}