using System.Text;
using System.Xml.Serialization;
using Medallion.Threading;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using Volo.Abp.Application.Services;
using Volo.Abp.Caching;
using Volo.Abp.Users;
using Yi.Framework.AiHub.Application.Contracts.Dtos.Fuwuhao;
using Yi.Framework.AiHub.Domain.Entities;
using Yi.Framework.AiHub.Domain.Managers.Fuwuhao;
using Yi.Framework.AiHub.Domain.Shared.Consts;
using Yi.Framework.AiHub.Domain.Shared.Enums.Fuwuhao;
using Yi.Framework.Rbac.Application.Contracts.Dtos.Account;
using Yi.Framework.Rbac.Application.Contracts.IServices;
using Yi.Framework.SqlSugarCore.Abstractions;

namespace Yi.Framework.AiHub.Application.Services.Fuwuhao;

/// <summary>
/// 服务号服务
/// </summary>
public class FuwuhaoService : ApplicationService
{
    private readonly ILogger<FuwuhaoService> _logger;
    private readonly IHttpContextAccessor _accessor;
    private readonly FuwuhaoManager _fuwuhaoManager;
    private IDistributedCache<SceneCacheDto> _sceneCache;
    private IDistributedCache<string> _openIdToSceneCache;
    private ISqlSugarRepository<AiUserExtraInfoEntity> _userRepository;
    private IAccountService _accountService;
    private IFileService _fileService;
    public IDistributedLockProvider DistributedLock => LazyServiceProvider.LazyGetService<IDistributedLockProvider>();

    public FuwuhaoService(ILogger<FuwuhaoService> logger, IHttpContextAccessor accessor, FuwuhaoManager fuwuhaoManager,
        IDistributedCache<SceneCacheDto> sceneCache, IAccountService accountService, IFileService fileService,
        IDistributedCache<string> openIdToSceneCache, ISqlSugarRepository<AiUserExtraInfoEntity> userRepositroy)
    {
        _logger = logger;
        _accessor = accessor;
        _fuwuhaoManager = fuwuhaoManager;
        _sceneCache = sceneCache;
        _accountService = accountService;
        _fileService = fileService;
        _openIdToSceneCache = openIdToSceneCache;
        _userRepository = userRepositroy;
    }

    /// <summary>
    /// 服务器号测试回调
    /// </summary>
    /// <returns></returns>
    [HttpGet("fuwuhao/callback")]
    public async Task<string> GetCallbackAsync([FromQuery] string signature, [FromQuery] string timestamp,
        [FromQuery] string nonce, [FromQuery] string echostr)
    {
        return echostr;
    }

    /// <summary>
    /// 服务号关注回调
    /// </summary>
    /// <param name="signature"></param>
    /// <param name="timestamp"></param>
    /// <param name="nonce"></param>
    /// <returns></returns>
    [HttpPost("fuwuhao/callback")]
    public async Task<string> PostCallbackAsync([FromQuery] string signature, [FromQuery] string timestamp,
        [FromQuery] string nonce)
    {
        _fuwuhaoManager.ValidateCallback(signature, timestamp, nonce);
        var request = _accessor.HttpContext.Request;
        // 1. 读取原始 XML 内容
        using var reader = new StreamReader(request.Body, Encoding.UTF8);
        var xmlString = await reader.ReadToEndAsync();

        var serializer = new XmlSerializer(typeof(FuwuhaoCallModel));
        using var stringReader = new StringReader(xmlString);
        var body = (FuwuhaoCallModel)serializer.Deserialize(stringReader);

        //获取场景值，后续通过场景值设置缓存状态，前端轮询这个场景值用户是否已操作即可
        var scene = body.EventKey.Replace("qrscene_", "");
        if (!(body.Event is "SCAN" or "subscribe"))
        {
            throw new UserFriendlyException("当前回调只处理扫码 与 关注");
        }

        if (scene is null)
        {
            throw new UserFriendlyException("服务号返回无场景值");
        }

        //制作幂等
        await using (var handle =
                     await DistributedLock.TryAcquireLockAsync($"Yi:fuwuhao:callbacklock:{scene}"))
        {
            if (handle == null)
            {
                return "success"; // 跳过直接返回成功
            }

            var cache = await _sceneCache.GetAsync($"{FuwuhaoConst.SceneCacheKey}{scene}");
            if (cache == null)
            {
                return "success"; // 跳过直接返回成功
            }

            if (cache.SceneResult != SceneResultEnum.Wait)
            {
                return "success"; // 跳过直接返回成功
            }

            //根据操作类型，进行业务处理，返回处理结果,再写入缓存,10s过去，相当于用户10s扫完app后，轮询要在10秒内完成
            var scenResult =
                await _fuwuhaoManager.CallBackHandlerAsync(cache.SceneType, body.FromUserName, cache.UserId);
            cache.SceneResult = scenResult.SceneResult;
            cache.UserId = scenResult.UserId;
            await _sceneCache.SetAsync($"{FuwuhaoConst.SceneCacheKey}{scene}", cache,
                new DistributedCacheEntryOptions() { AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(120) });

            //如果是注册，将OpenId与Scene进行绑定，代表用户有30分钟进行注册
            if (scenResult.SceneResult == SceneResultEnum.Register)
            {
                await _openIdToSceneCache.SetAsync($"{FuwuhaoConst.OpenIdToSceneCacheKey}{body.FromUserName}", scene,
                    new DistributedCacheEntryOptions() { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30) });

                var replyMessage =
                    _fuwuhaoManager.BuildRegisterMessage(body.FromUserName);
                return replyMessage;
            }
        }

        return "success";
    }


    /// <summary>
    /// 创建带参数的二维码
    /// </summary>
    /// <returns>二维码URL</returns>
    [HttpPost("fuwuhao/qrcode")]
    public async Task<QrCodeOutput> GetQrCodeAsync([FromQuery] SceneTypeEnum sceneType)
    {
        if (sceneType == SceneTypeEnum.Bind && CurrentUser.Id is null)
        {
            throw new UserFriendlyException("绑定微信，需登录用户，请重新登录后重试");
        }

        //生成一个随机场景值
        var scene = Guid.NewGuid().ToString("N");
        var qrCodeUrl = await _fuwuhaoManager.CreateQrCodeAsync(scene);
        await _sceneCache.SetAsync($"{FuwuhaoConst.SceneCacheKey}{scene}", new SceneCacheDto()
        {
            UserId = CurrentUser.IsAuthenticated ? CurrentUser.GetId() : null,
            SceneType = sceneType
        }, new DistributedCacheEntryOptions() { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10) });
        return new QrCodeOutput()
        {
            QrCodeUrl = qrCodeUrl,
            Scene = scene
        };
    }

    /// <summary>
    /// 扫码登录/注册/绑定，轮询接口
    /// </summary>
    /// <param name="scene"></param>
    /// <returns></returns>
    [HttpGet("fuwuhao/qrcode/result")]
    public async Task<QrCodeResultOutput> GetQrCodeResultAsync([FromQuery] string scene)
    {
        var output = new QrCodeResultOutput();
        var cache = await _sceneCache.GetAsync($"{FuwuhaoConst.SceneCacheKey}{scene}");
        if (cache is null)
        {
            output.SceneResult = SceneResultEnum.Expired;
            return output;
        }

        output.SceneResult = cache.SceneResult;
        switch (output.SceneResult)
        {
            case SceneResultEnum.Login:
                if (cache.UserId is null)
                {
                    throw new ApplicationException("获取用户id异常，请重试");
                }

                var loginInfo = await _accountService.PostLoginAsync(cache.UserId!.Value);
                output.Token = loginInfo.Token;
                output.RefreshToken = loginInfo.RefreshToken;
                break;
        }

        return output;
    }

    /// <summary>
    /// 注册账号，需要微信code
    /// </summary>
    /// <param name="code"></param>
    /// <returns></returns>
    [HttpGet("fuwuhao/register")]
    public async Task<IActionResult> RegisterByCodeAsync([FromQuery] string code)
    {
        var message = await RegisterByCodeForMessageAsync(code);
        //var message = "恭喜注册";
        var filePath = Path.Combine("wwwroot", "aihub", "auth.html");
        var html = await File.ReadAllTextAsync(filePath);
        var result = new ContentResult
        {
            Content = html.Replace("{{message}}", message),
            ContentType = "text/html",
            StatusCode = 200
        };
        return result;
    }


    private async Task<string> RegisterByCodeForMessageAsync(string code)
    {
        //根据code获取到openid、微信用户昵称、头像
        var userInfo = await _fuwuhaoManager.GetUserInfoByCodeAsync(code);
        if (userInfo is null)
        {
            return "当前注册已经失效，请重新扫码注册！";
        }

        var scene = await _openIdToSceneCache.GetAsync($"{FuwuhaoConst.OpenIdToSceneCacheKey}{userInfo.OpenId}");
        if (scene is null)
        {
            return "当前注册已经过期，请重新扫码注册！";
        }

        var files = new FormFileCollection();
        // 下载头像并添加到系统文件中
        if (!string.IsNullOrEmpty(userInfo.HeadImgUrl))
        {
            using var httpClient = new HttpClient();
            var imageBytes = await httpClient.GetByteArrayAsync(userInfo.HeadImgUrl);
            var imageStream = new MemoryStream(imageBytes);

            // 从URL中提取文件扩展名，默认为png
            var fileName = $"avatar_{userInfo.OpenId}.png";
            var formFile = new FormFile(imageStream, 0, imageBytes.Length, "avatar", fileName)
            {
                Headers = new HeaderDictionary(),
                ContentType = "image/png"
            };
            files.Add(formFile);
        }

        var result = await _fileService.Post(files);

        //由于存在查询/编辑在同一个事务操作，上锁防止并发
        await using (await DistributedLock.AcquireLockAsync($"fuwuhao:RegisterLock:{userInfo.OpenId}",
                         TimeSpan.FromMinutes(1)))
        {
            if (await _userRepository.IsAnyAsync(x => x.FuwuhaoOpenId == userInfo.OpenId))
            {
                return "你已注册过意社区账号！";
            }

            Guid userId;
            try
            {
                userId = await _accountService.PostSystemRegisterAsync(new RegisterDto
                {
                    UserName = $"wx{Random.Shared.Next(100000, 999999)}",
                    Password = Guid.NewGuid().ToString("N"),
                    Phone = null,
                    Email = null,
                    Nick = userInfo.Nickname,
                    Icon = result.FirstOrDefault()?.Id.ToString()
                });
            }
            catch (UserFriendlyException e)
            {
                return e.Message;
            }


            await _userRepository.InsertAsync(new AiUserExtraInfoEntity(userId, userInfo.OpenId));
            await _sceneCache.SetAsync($"{FuwuhaoConst.SceneCacheKey}{scene}", new SceneCacheDto
                {
                    UserId = userId,
                    SceneResult = SceneResultEnum.Login
                },
                new DistributedCacheEntryOptions() { AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(120) });
        }

        return "恭喜你成功注册意社区账号！";
    }
}

[XmlRoot("xml")]
public class FuwuhaoCallModel
{
    [XmlElement("ToUserName")] public string ToUserName { get; set; }
    [XmlElement("FromUserName")] public string FromUserName { get; set; }
    [XmlElement("CreateTime")] public string CreateTime { get; set; }
    [XmlElement("MsgType")] public string MsgType { get; set; }
    [XmlElement("Event")] public string Event { get; set; }
    [XmlElement("EventKey")] public string EventKey { get; set; }
    [XmlElement("Ticket")] public string Ticket { get; set; }
}