using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Memory;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Tokens;
using System.Collections.Concurrent;

namespace CloudGaming.Code.MiddlewareExtend
{
    /// <summary>
    /// 内存缓存
    /// </summary>
    public class MemoryCacheMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly IMemoryCache _cache;
        private static readonly ConcurrentDictionary<Endpoint, MemoryCacheAttribute> _attributeCache = new();
        /// <summary>
        /// 
        /// </summary>
        /// <param name="next"></param>
        /// <param name="cache"></param>
        public MemoryCacheMiddleware(RequestDelegate next, IMemoryCache cache)
        {
            _next = next;
            _cache = cache;
        }

        public async Task InvokeAsync(HttpContext context,
            IServiceProvider _serviceProvider,
            AppConfig appConfig
            )
        {
            // 检查当前请求是否需要缓存
            var cacheAttribute = GetCacheAttribute(context);
            if (cacheAttribute == null)
            {
                // 如果没有找到 MemoryCacheAttribute，直接调用下一个中间件
                await _next(context);
                return;
            }

            // 生成缓存键（基于请求路径和查询字符串）
            var cacheKey = GenerateCacheKey(context.Request, appConfig);

            // 尝试从缓存中获取数据
            if (_cache.TryGetValue(cacheKey, out string cachedResponse))
            {
                context.Response.StatusCode = StatusCodes.Status200OK;
                // 如果缓存中有数据，则直接返回缓存的响应
                context.Response.ContentType = "application/json";
                await context.Response.WriteAsync(cachedResponse);
                return;
            }

            // 捕获原始响应流
            var originalResponseStream = context.Response.Body;

            try
            {
                using (var memoryStream = new MemoryStream())
                {
                    context.Response.Body = memoryStream;

                    // 调用下一个中间件
                    await _next(context);

                    // 将响应流重新定位到起始位置
                    memoryStream.Position = 0;

                    // 读取响应内容
                    var responseBody = await new StreamReader(memoryStream).ReadToEndAsync();

                    // 将响应内容缓存
                    _cache.Set(cacheKey, responseBody, cacheAttribute.TimeSpan);

                    // 将内容写回原始响应流
                    memoryStream.Position = 0;
                    await memoryStream.CopyToAsync(originalResponseStream);
                }
            }
            finally
            {
                // 恢复原始响应流
                context.Response.Body = originalResponseStream;
            }
        }

        private MemoryCacheAttribute GetCacheAttribute(HttpContext context)
        {
            var endpoint = context.Features.Get<IEndpointFeature>()?.Endpoint;
            if (endpoint == null) return null;

            // 使用字典缓存以减少重复获取
            if (_attributeCache.TryGetValue(endpoint, out var cachedAttribute))
            {
                return cachedAttribute;
            }

            // 获取并缓存 MemoryCacheAttribute
            var attribute = endpoint.Metadata.GetMetadata<MemoryCacheAttribute>();
            if (attribute != null)
            {
                _attributeCache.TryAdd(endpoint, attribute);
            }

            return attribute;
        }

        private string GenerateCacheKey(HttpRequest request, AppConfig appConfig)
        {
            var appRequestInfo = new AppRequestConfig(request);
            // 基于请求路径和查询参数生成缓存键
            var cacheKey = $"{appConfig.Identifier}:{request.Path.Value.Replace('/', '.').TrimStart('.')}:{(string.IsNullOrEmpty(request.QueryString.Value) ? "default" : request.QueryString)}:{appRequestInfo.Key}";
            return cacheKey;
        }
    }
    /// <summary>
    /// 内存缓存特性类
    /// </summary>
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
    public class MemoryCacheAttribute : Attribute
    {

        public TimeSpan TimeSpan { get; }
        public MemoryCacheAttribute(int durationInSeconds)
        {
            TimeSpan = TimeSpan.FromSeconds(durationInSeconds);
        }
        public MemoryCacheAttribute(int hours, int minutes, int seconds)
        {

            TimeSpan = new TimeSpan(hours, minutes, seconds);
        }
        public MemoryCacheAttribute(int minutes, int seconds)
        {

            TimeSpan = new TimeSpan(0, minutes, seconds);
        }
    }

}
