using System.Security.Cryptography;
using System.Text.Json;
using HoneyBox.Core.Interfaces;
using HoneyBox.Model.Data;
using HoneyBox.Model.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using QRCoder;
using SkiaSharp;

namespace HoneyBox.Infrastructure.External.Poster;

/// <summary>
/// 海报生成服务实现
/// </summary>
public class PosterService : IPosterService
{
    private readonly HoneyBoxDbContext _dbContext;
    private readonly IImageUploadService _imageUploadService;
    private readonly IWechatService _wechatService;
    private readonly ILogger<PosterService> _logger;
    private readonly HttpClient _httpClient;

    // 默认缓存100天
    private const int DefaultCacheExpireDays = 100;
    // 二维码在海报上的位置（可通过配置调整）
    private const int DefaultQrCodeX = 104;
    private const int DefaultQrCodeY = 1180;
    private const int DefaultQrCodeSize = 200;

    public PosterService(
        HoneyBoxDbContext dbContext,
        IImageUploadService imageUploadService,
        IWechatService wechatService,
        IHttpClientFactory httpClientFactory,
        ILogger<PosterService> logger)
    {
        _dbContext = dbContext;
        _imageUploadService = imageUploadService;
        _wechatService = wechatService;
        _httpClient = httpClientFactory.CreateClient();
        _logger = logger;
    }

    /// <inheritdoc />
    public async Task<PosterResult> GetUserPosterAsync(int userId, string? platform = null)
    {
        try
        {
            // 默认平台
            platform ??= "H5";

            // 1. 验证用户ID
            if (userId <= 0)
            {
                return new PosterResult { Success = false, Message = "无效的用户ID" };
            }

            // 2. 获取海报配置
            var posterConfig = await GetPosterConfigAsync();
            if (posterConfig == null || string.IsNullOrEmpty(posterConfig.TemplateUrl))
            {
                return new PosterResult { Success = false, Message = "海报模板未配置" };
            }

            // 3. 计算模板哈希（用于缓存失效判断）
            var templateHash = ComputeHash(posterConfig.TemplateUrl);

            // 4. 获取小程序配置（用于生成小程序码）
            var appId = await GetDefaultAppIdAsync();

            // 5. 查询缓存记录
            var cacheRecord = await FindValidCacheAsync(userId, templateHash, appId, platform);
            if (cacheRecord != null)
            {
                _logger.LogInformation("用户 {UserId} 海报缓存命中", userId);
                return new PosterResult
                {
                    Success = true,
                    Message = "海报获取成功",
                    ImageUrl = cacheRecord.CosUrl
                };
            }

            // 6. 生成推广链接或小程序码
            byte[]? qrCodeBytes;
            if (platform.Equals("MP-WEIXIN", StringComparison.OrdinalIgnoreCase))
            {
                // 小程序：调用微信API生成小程序码
                // 注意：未发布的小程序不能指定page参数，否则会报错41030
                // scene参数会在小程序onLoad中通过options.scene获取
                var scene = $"pid={userId}";
                qrCodeBytes = await _wechatService.GetWxaCodeUnlimitAsync(scene, null, posterConfig.QrCodeSize ?? DefaultQrCodeSize);
                if (qrCodeBytes == null)
                {
                    return new PosterResult { Success = false, Message = "生成小程序码失败" };
                }
            }
            else
            {
                // H5/APP：生成URL二维码
                var qrContent = await GeneratePromotionUrlAsync(userId, platform);
                if (string.IsNullOrEmpty(qrContent))
                {
                    return new PosterResult { Success = false, Message = "生成推广链接失败" };
                }
                qrCodeBytes = GenerateQrCodeImage(qrContent);
                if (qrCodeBytes == null)
                {
                    return new PosterResult { Success = false, Message = "生成二维码失败" };
                }
            }

            // 7. 下载海报模板
            var templateBytes = await DownloadImageAsync(posterConfig.TemplateUrl);
            if (templateBytes == null)
            {
                return new PosterResult { Success = false, Message = "下载海报模板失败" };
            }

            // 8. 生成带二维码的海报
            var posterBytes = GeneratePosterWithQrCodeBytes(
                templateBytes,
                qrCodeBytes,
                posterConfig.QrCodeX ?? DefaultQrCodeX,
                posterConfig.QrCodeY ?? DefaultQrCodeY,
                posterConfig.QrCodeSize ?? DefaultQrCodeSize);

            if (posterBytes == null)
            {
                return new PosterResult { Success = false, Message = "海报生成失败" };
            }

            // 9. 上传到COS
            using var stream = new MemoryStream(posterBytes);
            var cosUrl = await _imageUploadService.UploadStreamAsync(
                stream,
                $"poster_{userId}_{platform}.png",
                "image/png",
                "poster");

            if (string.IsNullOrEmpty(cosUrl))
            {
                return new PosterResult { Success = false, Message = "海报上传失败" };
            }

            // 10. 失效旧缓存
            await InvalidateOldCachesAsync(userId, templateHash, platform);

            // 11. 保存新缓存记录
            var newCache = new UserPosterCache
            {
                UserId = userId,
                AppId = appId,
                TemplateHash = templateHash,
                CosUrl = cosUrl,
                FileSize = posterBytes.Length,
                MimeType = "image/png",
                Status = 1,
                ExpiresAt = DateTime.Now.AddDays(DefaultCacheExpireDays),
                Platform = platform,
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now
            };
            _dbContext.Set<UserPosterCache>().Add(newCache);
            await _dbContext.SaveChangesAsync();

            // 12. 随机清理过期缓存（5%概率）
            if (Random.Shared.Next(100) < 5)
            {
                _ = CleanExpiredCachesAsync();
            }

            _logger.LogInformation("用户 {UserId} 海报生成成功: {Url}", userId, cosUrl);
            return new PosterResult
            {
                Success = true,
                Message = "海报生成成功",
                ImageUrl = cosUrl
            };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "获取用户海报异常: UserId={UserId}", userId);
            return new PosterResult { Success = false, Message = "系统异常，请稍后重试" };
        }
    }

    #region 私有方法

    /// <summary>
    /// 获取海报配置
    /// </summary>
    private async Task<PosterConfig?> GetPosterConfigAsync()
    {
        var config = await _dbContext.Configs
            .FirstOrDefaultAsync(c => c.ConfigKey == "base");

        if (config?.ConfigValue == null)
        {
            return null;
        }

        try
        {
            var jsonDoc = JsonSerializer.Deserialize<JsonElement>(config.ConfigValue);
            return new PosterConfig
            {
                TemplateUrl = GetJsonString(jsonDoc, "poster_template"),
                QrCodeX = GetJsonInt(jsonDoc, "poster_qr_x"),
                QrCodeY = GetJsonInt(jsonDoc, "poster_qr_y"),
                QrCodeSize = GetJsonInt(jsonDoc, "poster_qr_size"),
                SiteUrl = GetJsonString(jsonDoc, "site_url")
            };
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "解析海报配置失败");
            return null;
        }
    }

    /// <summary>
    /// 获取默认小程序AppId
    /// </summary>
    private async Task<string> GetDefaultAppIdAsync()
    {
        var config = await _dbContext.Configs
            .FirstOrDefaultAsync(c => c.ConfigKey == "miniprogram_setting");

        if (config?.ConfigValue == null)
        {
            return string.Empty;
        }

        try
        {
            var jsonDoc = JsonSerializer.Deserialize<JsonElement>(config.ConfigValue);
            if (jsonDoc.TryGetProperty("miniprograms", out var miniprograms) &&
                miniprograms.ValueKind == JsonValueKind.Array)
            {
                foreach (var mp in miniprograms.EnumerateArray())
                {
                    if (mp.TryGetProperty("is_default", out var isDefault) &&
                        isDefault.GetInt32() == 1 &&
                        mp.TryGetProperty("appid", out var appid))
                    {
                        return appid.GetString() ?? string.Empty;
                    }
                }
            }
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "解析小程序配置失败");
        }

        return string.Empty;
    }

    /// <summary>
    /// 生成推广链接
    /// </summary>
    private async Task<string> GeneratePromotionUrlAsync(int userId, string platform)
    {
        // 获取站点URL配置
        var config = await _dbContext.Configs
            .FirstOrDefaultAsync(c => c.ConfigKey == "base");

        var siteUrl = "https://zfunbox.cn";
        if (config?.ConfigValue != null)
        {
            try
            {
                var jsonDoc = JsonSerializer.Deserialize<JsonElement>(config.ConfigValue);
                if (jsonDoc.TryGetProperty("site_url", out var siteUrlProp))
                {
                    var url = siteUrlProp.GetString();
                    if (!string.IsNullOrEmpty(url))
                    {
                        siteUrl = url;
                    }
                }
            }
            catch { }
        }

        // H5和APP使用URL链接
        return $"{siteUrl}?pid={userId}";
    }

    /// <summary>
    /// 下载图片
    /// </summary>
    private async Task<byte[]?> DownloadImageAsync(string url)
    {
        try
        {
            var response = await _httpClient.GetAsync(url);
            if (response.IsSuccessStatusCode)
            {
                return await response.Content.ReadAsByteArrayAsync();
            }
            _logger.LogWarning("下载图片失败: {Url}, StatusCode: {StatusCode}", url, response.StatusCode);
            return null;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "下载图片异常: {Url}", url);
            return null;
        }
    }

    /// <summary>
    /// 生成二维码图片字节数组
    /// </summary>
    private byte[]? GenerateQrCodeImage(string content)
    {
        try
        {
            using var qrGenerator = new QRCodeGenerator();
            var qrCodeData = qrGenerator.CreateQrCode(content, QRCodeGenerator.ECCLevel.M);
            using var qrCode = new PngByteQRCode(qrCodeData);
            return qrCode.GetGraphic(20);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "生成二维码失败: {Content}", content);
            return null;
        }
    }

    /// <summary>
    /// 生成带二维码的海报（使用二维码字节数组）
    /// </summary>
    private byte[]? GeneratePosterWithQrCodeBytes(byte[] templateBytes, byte[] qrCodeBytes, int qrX, int qrY, int qrSize)
    {
        try
        {
            // 加载模板图片
            using var templateBitmap = SKBitmap.Decode(templateBytes);
            if (templateBitmap == null)
            {
                _logger.LogWarning("无法解码海报模板图片");
                return null;
            }

            // 解码二维码/小程序码图片
            using var qrBitmap = SKBitmap.Decode(qrCodeBytes);
            if (qrBitmap == null)
            {
                _logger.LogWarning("无法解码二维码图片");
                return null;
            }

            // 缩放二维码到指定大小
            using var scaledQrBitmap = qrBitmap.Resize(new SKImageInfo(qrSize, qrSize), SKFilterQuality.High);

            // 创建画布
            using var surface = SKSurface.Create(new SKImageInfo(templateBitmap.Width, templateBitmap.Height));
            var canvas = surface.Canvas;

            // 绘制模板
            canvas.DrawBitmap(templateBitmap, 0, 0);

            // 绘制二维码
            canvas.DrawBitmap(scaledQrBitmap, qrX, qrY);

            // 导出为PNG
            using var image = surface.Snapshot();
            using var data = image.Encode(SKEncodedImageFormat.Png, 90);
            return data.ToArray();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "生成海报失败");
            return null;
        }
    }

    /// <summary>
    /// 生成带二维码的海报（使用二维码内容字符串，保留兼容）
    /// </summary>
    private byte[]? GeneratePosterWithQrCode(byte[] templateBytes, string qrContent, int qrX, int qrY, int qrSize)
    {
        try
        {
            // 加载模板图片
            using var templateBitmap = SKBitmap.Decode(templateBytes);
            if (templateBitmap == null)
            {
                _logger.LogWarning("无法解码海报模板图片");
                return null;
            }

            // 生成二维码
            using var qrGenerator = new QRCodeGenerator();
            var qrCodeData = qrGenerator.CreateQrCode(qrContent, QRCodeGenerator.ECCLevel.M);
            using var qrCode = new PngByteQRCode(qrCodeData);
            var qrCodeBytes = qrCode.GetGraphic(20);

            // 解码二维码图片
            using var qrBitmap = SKBitmap.Decode(qrCodeBytes);
            if (qrBitmap == null)
            {
                _logger.LogWarning("无法解码二维码图片");
                return null;
            }

            // 缩放二维码到指定大小
            using var scaledQrBitmap = qrBitmap.Resize(new SKImageInfo(qrSize, qrSize), SKFilterQuality.High);

            // 创建画布
            using var surface = SKSurface.Create(new SKImageInfo(templateBitmap.Width, templateBitmap.Height));
            var canvas = surface.Canvas;

            // 绘制模板
            canvas.DrawBitmap(templateBitmap, 0, 0);

            // 绘制二维码
            canvas.DrawBitmap(scaledQrBitmap, qrX, qrY);

            // 导出为PNG
            using var image = surface.Snapshot();
            using var data = image.Encode(SKEncodedImageFormat.Png, 90);
            return data.ToArray();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "生成海报失败");
            return null;
        }
    }

    /// <summary>
    /// 查找有效缓存
    /// </summary>
    private async Task<UserPosterCache?> FindValidCacheAsync(int userId, string templateHash, string appId, string platform)
    {
        return await _dbContext.Set<UserPosterCache>()
            .Where(c => c.UserId == userId)
            .Where(c => c.TemplateHash == templateHash)
            .Where(c => c.Platform == platform)
            .Where(c => c.Status == 1)
            .Where(c => c.ExpiresAt > DateTime.Now)
            .OrderByDescending(c => c.Id)
            .FirstOrDefaultAsync();
    }

    /// <summary>
    /// 失效旧缓存
    /// </summary>
    private async Task InvalidateOldCachesAsync(int userId, string templateHash, string platform)
    {
        var oldCaches = await _dbContext.Set<UserPosterCache>()
            .Where(c => c.UserId == userId)
            .Where(c => c.Platform == platform)
            .Where(c => c.TemplateHash != templateHash || c.Status == 1)
            .ToListAsync();

        foreach (var cache in oldCaches)
        {
            cache.Status = 0;
            cache.UpdatedAt = DateTime.Now;
        }

        if (oldCaches.Count > 0)
        {
            await _dbContext.SaveChangesAsync();
        }
    }

    /// <summary>
    /// 清理过期缓存
    /// </summary>
    private async Task CleanExpiredCachesAsync()
    {
        try
        {
            var expiredCaches = await _dbContext.Set<UserPosterCache>()
                .Where(c => c.ExpiresAt < DateTime.Now || c.Status == 0)
                .Take(100)
                .ToListAsync();

            if (expiredCaches.Count > 0)
            {
                _dbContext.Set<UserPosterCache>().RemoveRange(expiredCaches);
                await _dbContext.SaveChangesAsync();
                _logger.LogInformation("清理了 {Count} 条过期海报缓存", expiredCaches.Count);
            }
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "清理过期海报缓存失败");
        }
    }

    /// <summary>
    /// 计算字符串哈希
    /// </summary>
    private static string ComputeHash(string input)
    {
        var bytes = System.Text.Encoding.UTF8.GetBytes(input);
        var hash = MD5.HashData(bytes);
        return Convert.ToHexString(hash).ToLowerInvariant();
    }

    private static string? GetJsonString(JsonElement element, string propertyName)
    {
        if (element.TryGetProperty(propertyName, out var prop) && prop.ValueKind == JsonValueKind.String)
        {
            return prop.GetString();
        }
        return null;
    }

    private static int? GetJsonInt(JsonElement element, string propertyName)
    {
        if (element.TryGetProperty(propertyName, out var prop))
        {
            if (prop.ValueKind == JsonValueKind.Number)
            {
                return prop.GetInt32();
            }
            if (prop.ValueKind == JsonValueKind.String && int.TryParse(prop.GetString(), out var val))
            {
                return val;
            }
        }
        return null;
    }

    #endregion

    /// <summary>
    /// 海报配置
    /// </summary>
    private class PosterConfig
    {
        public string? TemplateUrl { get; set; }
        public int? QrCodeX { get; set; }
        public int? QrCodeY { get; set; }
        public int? QrCodeSize { get; set; }
        public string? SiteUrl { get; set; }
    }
}
