using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp.Caching;
using Volo.Abp.Domain.Services;
using Yi.Framework.AiHub.Domain.Entities;
using Yi.Framework.AiHub.Domain.Shared.Enums.Fuwuhao;
using Yi.Framework.SqlSugarCore.Abstractions;

namespace Yi.Framework.AiHub.Domain.Managers.Fuwuhao;

public class FuwuhaoManager : DomainService
{
    private readonly FuwuhaoOptions _options;
    private readonly IHttpClientFactory _httpClientFactory;
    private IDistributedCache<AccessTokenResponse> _accessTokenCache;
    private ISqlSugarRepository<AiUserExtraInfoEntity> _userRepository;
    private readonly ILogger<FuwuhaoManager> _logger;

    public FuwuhaoManager(IOptions<FuwuhaoOptions> options, IHttpClientFactory httpClientFactory,
        ISqlSugarRepository<AiUserExtraInfoEntity> userRepository,
        IDistributedCache<AccessTokenResponse> accessTokenCache, ILogger<FuwuhaoManager> logger)
    {
        _options = options.Value;
        _httpClientFactory = httpClientFactory;
        _userRepository = userRepository;
        _accessTokenCache = accessTokenCache;
        _logger = logger;
    }

    /// <summary>
    /// 获取微信公众号AccessToken
    /// </summary>
    /// <returns>AccessToken响应对象</returns>
    private async Task<string> GetAccessTokenAsync()
    {
        var output = await _accessTokenCache.GetOrAddAsync("Fuwuhao", async () =>
        {
            var url =
                $"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={_options.AppId}&secret={_options.Secret}";

            var response = await _httpClientFactory.CreateClient().GetAsync(url);
            response.EnsureSuccessStatusCode();

            var jsonContent = await response.Content.ReadAsStringAsync();
            var result = JsonSerializer.Deserialize<AccessTokenResponse>(jsonContent, new JsonSerializerOptions
            {
                PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
            });
            if (result is null || string.IsNullOrEmpty(result.AccessToken))
            {
                throw new UserFriendlyException("微信服务号AccessToken为空");
            }

            return result;
        }, () => new DistributedCacheEntryOptions()
        {
            AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(3600)
        });

        return output.AccessToken;
    }

    /// <summary>
    /// 创建带参数的二维码
    /// </summary>
    /// <param name="scene">场景值</param>
    /// <returns>二维码URL</returns>
    public async Task<string> CreateQrCodeAsync(string scene)
    {
        var accessToken = await GetAccessTokenAsync();
        var url = $"https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={accessToken}";

        var requestBody = new
        {
            action_name = "QR_STR_SCENE",
            expire_seconds = 600,
            action_info = new
            {
                scene = new
                {
                    scene_str = scene
                }
            }
        };

        var jsonContent = JsonSerializer.Serialize(requestBody);
        var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");

        var response = await _httpClientFactory.CreateClient().PostAsync(url, content);
        response.EnsureSuccessStatusCode();

        var responseContent = await response.Content.ReadAsStringAsync();
        var result = JsonSerializer.Deserialize<QrCodeResponse>(responseContent);

        return $"https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket={result.Ticket}";
    }

    /// <summary>
    /// 通过code获取用户基础信息接口（为了获取用户access_token和openid）
    /// </summary>
    /// <param name="code">授权码</param>
    /// <returns>用户基础信息响应对象</returns>
    private async Task<UserBaseInfoResponse> GetBaseUserInfoByCodeAsync(string code)
    {
        var url =
            $"https://api.weixin.qq.com/sns/oauth2/access_token?appid={_options.AppId}&secret={_options.Secret}&code={code}&grant_type=authorization_code";

        var response = await _httpClientFactory.CreateClient().GetAsync(url);
        response.EnsureSuccessStatusCode();

        var jsonContent = await response.Content.ReadAsStringAsync();

        _logger.LogInformation($"服务号code获取用户基础信息：{jsonContent}");
        var result = JsonSerializer.Deserialize<UserBaseInfoResponse>(jsonContent);

        return result;
    }

    /// <summary>
    /// 通过code获取用户信息接口
    /// </summary>
    /// <param name="code">授权码</param>
    /// <returns>用户信息响应对象</returns>
    public async Task<UserInfoResponse?> GetUserInfoByCodeAsync(string code)
    {
        var baseUserInfo = await GetBaseUserInfoByCodeAsync(code);
        var url =
            $"https://api.weixin.qq.com/sns/userinfo?access_token={baseUserInfo.AccessToken}&openid={baseUserInfo.OpenId}&lang=zh_CN";

        var response = await _httpClientFactory.CreateClient().GetAsync(url);
        response.EnsureSuccessStatusCode();

        var jsonContent = await response.Content.ReadAsStringAsync();
        _logger.LogInformation($"服务号code获取用户详细信息：{jsonContent}");
        var result = JsonSerializer.Deserialize<UserInfoResponse>(jsonContent);
        return result;
    }

    /// <summary>
    /// 校验微信服务器回调参数是否正确
    /// </summary>
    /// <param name="signature">微信加密签名</param>
    /// <param name="timestamp">时间戳</param>
    /// <param name="nonce">随机数</param>
    /// <returns>true表示验证通过，false表示验证失败</returns>
    public void ValidateCallback(string signature, string timestamp, string nonce)
    {
        var token = _options.CallbackToken; // 您设置的token

        // 将token、timestamp、nonce三个参数进行字典序排序
        var parameters = new[] { token, timestamp, nonce };
        Array.Sort(parameters);

        // 将三个参数字符串拼接成一个字符串
        var concatenated = string.Join("", parameters);

        // 进行SHA1计算签名
        using (var sha1 = SHA1.Create())
        {
            var hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(concatenated));
            var calculatedSignature = BitConverter.ToString(hash).Replace("-", "").ToLower();

            // 与URL链接中的signature参数进行对比
            var result = calculatedSignature.Equals(signature, StringComparison.OrdinalIgnoreCase);
            if (!result)
            {
                throw new UserFriendlyException("服务号回调签名异常");
            }
        }
    }


    /// <summary>
    /// 构建引导注册图文消息体
    /// </summary>
    /// <param name="toUser">接收用户的OpenID</param>
    /// <param name="title">图文消息标题</param>
    /// <param name="description">图文消息描述</param>
    /// <returns>XML格式的图文消息体</returns>
    public string BuildRegisterMessage(string toUser, string title = "意社区点击一键注册账号",
        string description = "来自意社区SSO统一注册安全中心")
    {
        var createTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
        var fromUser = _options.FromUser;
        var url =
            $"https://open.weixin.qq.com/connect/oauth2/authorize?appid={_options.AppId}&redirect_uri={_options.RedirectUri}&response_type=code&scope=snsapi_userinfo&state={createTime}#wechat_redirect";
        var xml = $@"<xml>
  <ToUserName><![CDATA[{toUser}]]></ToUserName>
  <FromUserName><![CDATA[{fromUser}]]></FromUserName>
  <CreateTime>{createTime}</CreateTime>
  <MsgType><![CDATA[news]]></MsgType>
  <ArticleCount>1</ArticleCount>
  <Articles>
    <item>
      <Title><![CDATA[{title}]]></Title>
      <Description><![CDATA[{description}]]></Description>
      <PicUrl><![CDATA[{_options.PicUrl}]]></PicUrl>
      <Url><![CDATA[{url}]]></Url>
    </item>
  </Articles>
</xml>";
        return xml;
    }


    /// <summary>
    /// 处理回调逻辑
    /// </summary>
    /// <param name="sceneType"></param>
    /// <param name="openId"></param>
    /// <param name="bindUserId"></param>
    /// <returns></returns>
    public async Task<(SceneResultEnum SceneResult, Guid? UserId)> CallBackHandlerAsync(SceneTypeEnum sceneType,
        string openId, Guid? bindUserId)
    {
        var aiUserInfo = await _userRepository._DbQueryable.Where(x => x.FuwuhaoOpenId == openId).FirstAsync();
        switch (sceneType)
        {
            case SceneTypeEnum.LoginOrRegister:
                //有openid，说明登录成功
                if (aiUserInfo is not null)
                {
                    return (SceneResultEnum.Login, aiUserInfo.UserId);
                }
                //无openid，说明需要进行注册
                else
                {
                    return (SceneResultEnum.Register, null);
                }

                break;
            case SceneTypeEnum.Bind:
                //说明已经有微信号，直接换绑

                if (aiUserInfo is not null)
                {
                    await _userRepository.DeleteByIdAsync(aiUserInfo.Id);
                }

                if (bindUserId is null)
                {
                    throw new UserFriendlyException("绑定用户，需要传入绑定的用户id");
                }

                //说明没有绑定过，直接绑定
                await _userRepository.InsertAsync(new AiUserExtraInfoEntity(bindUserId.Value, openId));
                return (SceneResultEnum.Bind, bindUserId);
                break;
            default:
                throw new ArgumentOutOfRangeException(nameof(sceneType), sceneType, null);
        }
    }
}