using Newtonsoft.Json;

using StackExchange.Redis;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace HuanMeng.DotNetCore.WeChat
{
    /// <summary>
    /// 小程序帮助类
    /// </summary>
    /// <param name="appId"></param>
    /// <param name="secret"></param>
    /// <param name="database"></param>
    /// <param name="_httpClientFactory"></param>
    public class MiniProgram(string appId, string secret, IDatabase database, IHttpClientFactory? _httpClientFactory)
    {
        public string AppId { get; set; } = appId;
        string key = $"WeChat:{appId}";
        // Method to get user's OpenID
        public async Task<(string openId, string unionid, string session_key)> GetOpenid(string code)
        {
            string url = $"https://api.weixin.qq.com/sns/jscode2session?appid={appId}&secret={secret}&js_code={code}&grant_type=authorization_code";
            var resUserInfo = await GetCurlData(url);

            if (resUserInfo.ContainsKey("errcode"))
            {
                return (null, null, null);
            }

            string openid = resUserInfo.GetValueOrDefault("openid")?.ToString() ?? "";
            string unionid = resUserInfo.GetValueOrDefault("unionid")?.ToString() ?? "";
            string session_key = resUserInfo.GetValueOrDefault("session_key")?.ToString() ?? "";

            return new(openid, unionid, session_key);

        }


        // Get user info based on access token and openid
        public async Task<dynamic> GetUserInfoAsync(string openid)
        {
            var accessToken = await GetAccessToken();
            string url = $"https://api.weixin.qq.com/sns/userinfo?access_token={accessToken}&openid={openid}&lang=zh_CN";
            var response = await GetCurlData(url);

            return response;
        }

        public async Task<string> GetAccessToken()
        {
            var accessTokenInfo = GetConfig();

            if (accessTokenInfo != null)
            {
                return accessTokenInfo?.Access_token;
            }
            else
            {
                string url = $"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appId}&secret={secret}";
                var resAccessToken = await GetCurlData(url);
                if (resAccessToken.ContainsKey("errcode"))
                {
                    throw new Exception("获取微信token失败");
                }

                string accessToken = resAccessToken["access_token"].ToString();
                int expiresIn = Convert.ToInt32(resAccessToken["expires_in"]);
                long accessTokenTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + expiresIn;

                var data = new AccessToken(accessToken, accessTokenTime);
                SetConfig(data);
                return accessToken;
            }
        }

        // Helper method for GET requests
        private async Task<Dictionary<string, object>> GetCurlData(string url)
        {
            using HttpClient client = _httpClientFactory.CreateClient();
            var response = await client.GetStringAsync(url);
            return JsonConvert.DeserializeObject<Dictionary<string, object>>(response);
        }

        // Helper method for POST requests
        private async Task<Dictionary<string, object>> PostCurlData(string url, object data)
        {
            using HttpClient client = _httpClientFactory.CreateClient();
            var json = JsonConvert.SerializeObject(data);
            var content = new StringContent(json, Encoding.UTF8, "application/json");
            var response = await client.PostAsync(url, content);
            var result = await response.Content.ReadAsStringAsync();
            return JsonConvert.DeserializeObject<Dictionary<string, object>>(result);
        }

        // Helper method for HTTP POST data
        private async Task<string> HttpPostData(string url, object data)
        {
            using HttpClient client = _httpClientFactory.CreateClient();
            var json = JsonConvert.SerializeObject(data);
            var content = new StringContent(json, Encoding.UTF8, "application/json");
            var response = await client.PostAsync(url, content);
            return await response.Content.ReadAsStringAsync();
        }

        private AccessToken? GetConfig()
        {

            var json = database.StringGet(key);
            if (!string.IsNullOrEmpty(json))
            {
                return JsonConvert.DeserializeObject<AccessToken>(json);
            }
            return null;
        }

        private void SetConfig(AccessToken access)
        {
            var outTime = new TimeSpan(0, 0, 7000);
            database.StringSet(key, JsonConvert.SerializeObject(access), outTime);
        }

    }

    /// <summary>
    /// 
    /// </summary>
    public class AccessToken
    {
        public string? Access_token { get; }
        public long Access_token_time { get; }

        public AccessToken(string? access_token, long access_token_time)
        {
            Access_token = access_token;
            Access_token_time = access_token_time;
        }
        public AccessToken()
        {

        }
    }
}
