using Furion;
using Furion.DataEncryption;
using Furion.DependencyInjection;
using Furion.DistributedIDGenerator;
using Mapster;
using Nirvana.Common;
using Nirvana.Common.ApiBase;
using Senparc.Weixin;
using Senparc.Weixin.WxOpen.AdvancedAPIs.Sns;
using Senparc.Weixin.WxOpen.Containers;
using Senparc.Weixin.WxOpen.Entities;
using Senparc.Weixin.WxOpen.Helpers;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using YBDevice.Body.BodyFatHelper;
using YBDevice.Body.Level;
using YBDevice.Entity;
using YBDevice.Entity.DataModel.ThirdOpen;
using YBDevice.NApi.Application.ThirdClient.Family;

namespace YBDevice.NApi.Application.ThirdClient
{
    /// <summary>
    /// 第三方相关处理
    /// </summary>
    public class OpenService : IOpenService, ITransient
    {
        private readonly IBodyFatHelperService _bodyFatHelperService;
        private readonly ILevelService _levelService;
        private readonly ITouTiaoHttp _touTiaoHttp;
        private readonly ISqlSugarRepository<YB_Family> repository;
        private readonly IOpenFamilyService _openFamilyService;
        private readonly SqlSugarClient dbClient;
        private string WxOpenAppId = Config.SenparcWeixinSetting.Items["childresultclient"].WxOpenAppId;
        private string WxOpenAppSecret = Config.SenparcWeixinSetting.Items["childresultclient"].WxOpenAppSecret;
        private readonly string TouTiaoAppId = App.Configuration["TouTiaoSettiong:AppId"];
        public OpenService(IBodyFatHelperService bodyFatHelperService, ILevelService levelService, ISqlSugarRepository<YB_Family> sqlSugarRepository, ITouTiaoHttp touTiaoHttp, IOpenFamilyService openFamilyService)
        {
            _bodyFatHelperService = bodyFatHelperService;
            _levelService = levelService;
            repository = sqlSugarRepository;
            dbClient = repository.Context;
            _touTiaoHttp = touTiaoHttp;
            _openFamilyService = openFamilyService;
        }
        /// <summary>
        /// BMI计算
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public async Task<ResultInfo> CalcBmiAsync(BmiInfoC2SDto data)
        {
            var bmi = _bodyFatHelperService.CalcBMi(data.Height, data.Weight);
            var standdata = await _levelService.LevelAsync(data.Sex, data.Birthday, bmi, LevelType.BMI);
            return new ResultInfo(ResultState.SUCCESS, "success", new
            {
                bmi = bmi,
                bmilevel = standdata.Level,
                bmilevelcolor = standdata.Color,
                bmilevellist = standdata.list
            });
        }
        /// <summary>
        /// 身高预测
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public async Task<ResultInfo> CalcPredictHeightAsync(HeightInfoC2SDto data)
        {
            var month = data.Birthday.ToMonth();
            //获取标准身高
            var heightdata = await dbClient.Queryable<YB_NewHeightStand>().Where(x => x.Sex == data.sex && x.Month <= month).OrderBy(x => x.Month, OrderByType.Desc).FirstAsync();
            //成年身高预测
            decimal adultheight = AdultHeight(data.DadHeight, data.MomHeight, data.sex);
            return new ResultInfo(ResultState.SUCCESS, "success", new
            {
                standheight = heightdata != null ? heightdata.median : 0,
                adultheight = adultheight
            });
        }

        /// <summary>
        /// 成年身高预测,女孩的靶身高=（父亲身高+母亲身高-13cm）÷2
        /// 男孩的靶身高=（父亲身高+母亲身高+13cm）÷2
        /// </summary>
        /// <param name="dadheight">父亲身高</param>
        /// <param name="momheight">母亲身高</param>
        /// <param name="sex">性别,1-男,2-女,0-未知</param>
        /// <returns></returns>
        private static decimal AdultHeight(decimal dadheight, decimal momheight, GenderType sex)
            => (dadheight, momheight, sex) switch
            {
                _ when dadheight <= 0 || momheight <= 0 => 0,
                _ when (sex == GenderType.FeMale) => (37.85m + 0.75m * (dadheight + momheight) / 2).ToDecimal(1),
                _ => (45.99m + 0.78m * (dadheight + momheight) / 2).ToDecimal(1)
            };
        /// <summary>
        /// 登录接口
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public async Task<ResultInfo> OnLoginAsync(TouTiaoLoginC2SDto data)
        {
            if (data.UA == UAConst.WeiXin)
            {
                return await WeiXinLogin(data);
            }
            else
            {
                return await TouTiaoLoginAsync(data);
            }
        }
        /// <summary>
        /// 生成token
        /// </summary>
        /// <returns></returns>
        private ResultInfo GenToken(Guid userid, string openid, string sessionid)
        {
            openid = openid.ToStr();
            sessionid = sessionid.ToStr();
            //生成token
            var token = JWTEncryption.Encrypt(new Dictionary<string, object>() {
                    {"UserId",userid},
                    {"OpenId",openid }
                });
            var refreshToken = JWTEncryption.GenerateRefreshToken(token, 43200);
            return new ResultInfo(ResultState.SUCCESS, "登录成功", new
            {
                token = token,
                sessionid = sessionid,
                refreshtoken = refreshToken
            });
        }
        /// <summary>
        /// 微信登录处理
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private async Task<ResultInfo> WeiXinLogin(TouTiaoLoginC2SDto data)
        {
            var jsonResult = await SnsApi.JsCode2JsonAsync(WxOpenAppId, WxOpenAppSecret, data.code);
            if (jsonResult.errcode == ReturnCode.请求成功)
            {
                //检查此openid是否已注册
                var wxdata = await dbClient.Queryable<T_UserWX>()
                    .Where(x => x.OpenId == jsonResult.openid)
                    .Select(x => new T_UserWX
                    {
                        UserId = x.UserId,
                        OpenId = x.OpenId
                    })
                    .FirstAsync()
                    ;
                TimeSpan val = new TimeSpan(1, 0, 0);//一个小时
                if (wxdata == null)
                {
                    //记录信息
                    var sessionBag = await SessionContainer.UpdateSessionAsync(jsonResult.openid, jsonResult.openid, jsonResult.session_key, jsonResult.unionid, val);
                    return new ResultInfo(ResultState.WXUNAUTHORITY, "此账户还未注册", new WxOpenLoginData
                    {
                        sessionid = sessionBag.Key
                    });
                }
                //刷新key
                var sessionbag = await SessionContainer.UpdateSessionAsync(jsonResult.openid, jsonResult.openid, jsonResult.session_key, jsonResult.unionid, val);
                //生成token
                return GenToken(wxdata.UserId, wxdata.OpenId, sessionbag.Key);
            }
            return new ResultInfo(ResultState.FAIL, jsonResult.errmsg);
        }
        /// <summary>
        /// 字节登录处理
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private async Task<ResultInfo> TouTiaoLoginAsync(TouTiaoLoginC2SDto data)
        {
            var requestdata = new TouTiaoCode2SessionRequest
            {
                anonymous_code = data.anonymous_code,
                code = data.code
            };
            string errmsg = string.Empty;
            var response = await _touTiaoHttp.JSCode2SessionAsync(requestdata, (res, error) =>
            {
                errmsg = error;
            });
            if (!string.IsNullOrEmpty(errmsg))
            {
                return new ResultInfo(ResultState.FAIL, errmsg);
            }
            var returnstr = await response.Content.ReadAsStringAsync();
            if (response.IsSuccessStatusCode)
            {
                var resdata = returnstr.ToObject<TouTiaoResponseData<TuTiaoLoginResponse>>();
                if (resdata.err_no != 0)
                {
                    return new ResultInfo(ResultState.FAIL, resdata.err_tips);
                }
                //检查此openid是否已注册
                var wxdata = await dbClient.Queryable<T_UserWX>()
                    .Where(x => x.OpenId == resdata.data.openid)
                    .Select(x => new T_UserWX
                    {
                        UserId = x.UserId,
                        OpenId = x.OpenId
                    })
                    .FirstAsync()
                    ;
                TimeSpan val = new TimeSpan(1, 0, 0);//一个小时
                if (wxdata == null)
                {
                    //记录信息
                    var sessionBag = await SessionContainer.UpdateSessionAsync(resdata.data.openid, resdata.data.openid, resdata.data.session_key, resdata.data.unionid, val);
                    return new ResultInfo(ResultState.WXUNAUTHORITY, "此账户还未注册", new WxOpenLoginData
                    {
                        sessionid = sessionBag.Key
                    });
                }
                //刷新key
                var sessionbag = await SessionContainer.UpdateSessionAsync(resdata.data.openid, resdata.data.openid, resdata.data.session_key, resdata.data.unionid, val);
                //生成token
                return GenToken(wxdata.UserId, wxdata.OpenId, sessionbag.Key);
            }
            return new ResultInfo(ResultState.FAIL, returnstr);
        }
        /// <summary>
        /// 获取测量记录列表
        /// </summary>
        /// <param name="familyid">家庭成员ID</param>
        /// <param name="page">页码</param>
        /// <param name="pagesize">每页显示数量</param>
        /// <returns></returns>
        public async Task<ResultInfo> GetResultListAsync(Guid familyid, int page, int pagesize)
        {
            var CurrentUser = AuthUserInfoService.GetUserInfo();
            RefAsync<int> totalnum = 0;
            var query = await dbClient.Queryable<T_Result>()
                .OrderBy(x => x.ResultTime, OrderByType.Desc)
                .Where(x => x.FamilyId == familyid)
                .Select(x => new T_Result
                {
                    Height = x.Height,
                    Weight = x.Weight,
                    BMI = x.BMI,
                    CreateTime = x.ResultTime
                })
                .ToPageListAsync(page, pagesize, totalnum);
            var result = query.Adapt<List<ResultListS2CDto>>();
            var list = new PageParms<ResultListS2CDto>
            {
                page = page,
                Items = result,
                totalnum = totalnum,
                limit = pagesize
            };
            return new ResultInfo(ResultState.SUCCESS, "success", list);
        }
        /// <summary>
        /// 获取用户信息
        /// </summary>
        /// <param name="familyid">家庭成员ID</param>
        /// <returns></returns>
        public async Task<ResultInfo> GetUserInfoAsync(Guid? familyid)
        {
            var CurrentUser = AuthUserInfoService.GetUserInfo();
            //检查是否有家庭成员ID
            if (!familyid.HasValue)
            {
                familyid = CurrentUser.UserId;
            }
            var family = await dbClient.Queryable<T_Family>().Where(x => x.Id == familyid)
                .Select(x => new T_Family
                {
                    Name = x.Name,
                    HeadImg = x.HeadImg
                })
                .FirstAsync();
            var familydata = await dbClient.Queryable<T_FamilyData>().Where(x => x.Id == familyid).Select(x => new T_FamilyData
            {
                Brithday = x.Brithday,
                Height = x.Height,
                LastResultTime = x.LastResultTime,
                Sex = x.Sex,
                Weight = x.Weight
            }).FirstAsync();
            var returndata = new UserInfoS2CDto
            {
                HeadImg = family != null ? family.HeadImg : "",
                Name = family != null ? family.Name : "小白",
                Sex = familydata != null ? (GenderType)familydata.Sex : GenderType.UnKnow,
                Birthday = familydata != null ? familydata.Brithday : null,
                Weight = familydata != null ? familydata.Weight : 0,
                Height = familydata != null ? familydata.Height : 0,
                LastResultTime = familydata != null ? familydata.LastResultTime : null,
                Age = familydata != null ? (familydata.Brithday.HasValue ? familydata.Brithday.Value.ToAAge() : "") : "",
                FamilyId = familyid.Value
            };
            returndata.HeadImg = !string.IsNullOrEmpty(returndata.HeadImg) ? returndata.HeadImg : _openFamilyService.HeadImg(returndata.Sex, 1);
            returndata.BMI = _bodyFatHelperService.CalcBMi(returndata.Height, returndata.Weight);
            var standdata = await _levelService.LevelAsync(returndata.Sex, returndata.Birthday, returndata.BMI, LevelType.BMI);
            returndata.BMILevel = standdata.Level;
            returndata.BMILevelColor = standdata.Color;
            returndata.BMILevelList = standdata.list;
            standdata = await _levelService.LevelAsync(returndata.Sex, returndata.Birthday, returndata.Height, LevelType.Height);
            returndata.HeightLevel = standdata.Level;
            returndata.HeightLevelColor = standdata.Color;
            returndata.HeightLevelList = standdata.list;
            standdata = await _levelService.LevelAsync(returndata.Sex, returndata.Birthday, returndata.Weight, LevelType.Weight);
            returndata.WeightLevel = standdata.Level;
            returndata.WeightLevelColor = standdata.Color;
            returndata.WeightLevelList = standdata.list;
            return new ResultInfo(ResultState.SUCCESS, "success", returndata);
        }
        /// <summary>
        /// 添加测量记录
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public async Task<ResultInfo> AddResultAsync(InsertResultC2SDto data)
        {
            var CurrentUser = AuthUserInfoService.GetUserInfo();
            decimal bmi = _bodyFatHelperService.CalcBMi(data.Height, data.Weight);
            if (data.CreateTime.HasValue)
            {
                var ctime = data.CreateTime.ToDate();
                string time = $"{ctime.Year}-{ctime.Month}-{ctime.Day} {DateTime.Now.Hour}:{DateTime.Now.Minute}:{DateTime.Now.Second}";
                data.CreateTime = time.ToDate();
            }
            else
            {
                data.CreateTime = DateTime.Now;
            }
            var insertresult = new T_Result
            {
                Height = data.Height,
                Weight = data.Weight,
                BMI = bmi,
                ResultTime = data.CreateTime.Value,
                Id = IDGen.NextID(),
                UserId = CurrentUser.UserId,
                FamilyId = data.FamilyId,
                CreateTime = DateTime.Now
            };
            await dbClient.Insertable(insertresult).ExecuteCommandAsync();
            var userdata = await dbClient.Queryable<T_FamilyData>().Where(x => x.Id == data.FamilyId)
                .Select(x => new T_FamilyData
                {
                    LastResultTime = x.LastResultTime
                })
                .FirstAsync()
                ;
            if (userdata != null)
            {
                if (!userdata.LastResultTime.HasValue || userdata.LastResultTime < insertresult.ResultTime)
                {
                    await dbClient.Updateable<T_FamilyData>().SetColumns(x => new T_FamilyData
                    {
                        Height = data.Height,
                        Weight = data.Weight,
                        LastResultTime = insertresult.ResultTime,
                        LastResultId = insertresult.Id
                    }).Where(x => x.Id == data.FamilyId).ExecuteCommandAsync();
                }
            }
            else
            {
                await dbClient.Insertable(new T_FamilyData
                {
                    Brithday = null,
                    Sex = 0,
                    Height = data.Height,
                    Id = data.FamilyId,
                    LastResultTime = insertresult.ResultTime,
                    Weight = data.Weight,
                    LastResultId = insertresult.Id
                }).ExecuteCommandAsync();
            }
            return new ResultInfo(ResultState.SUCCESS, "记录增加成功");
        }

        /// <summary>
        /// 用户注册
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public async Task<ResultInfo> RegisterAsync(SubmitUserInfoC2SDto data)
        {
            var sessionbage = await SessionContainer.GetSessionAsync(data.sessionId);
            if (sessionbage == null)
            {
                return new ResultInfo(ResultState.FAIL, "登录信息已过期");
            }
            //检查此openid是否已绑定用户
            var wxdata = await dbClient.Queryable<T_UserWX>().Where(x => x.OpenId == sessionbage.OpenId)
                .Select(x => new T_UserWX
                {
                    UserId = x.UserId
                })
                .FirstAsync()
                ;
            if (wxdata == null)
            {
                var user = new T_RegUser
                {
                    Id = IDGen.NextID(),
                    CreateTime = DateTime.Now,
                    HeadImg = data.avatarUrl,
                    Name = data.nickName
                };
                await dbClient.Insertable(user).ExecuteCommandAsync();

                var userwx = new T_UserWX
                {
                    Id = IDGen.NextID(),
                    OpenId = sessionbage.OpenId.ToStr(),
                    UserId = user.Id
                };
                await dbClient.Insertable(userwx).ExecuteCommandAsync();

                //增加一个默认家庭成员
                var family = new T_Family
                {
                    Id = user.Id,
                    CreateTime = user.CreateTime,
                    HeadImg = user.HeadImg,
                    Name = user.Name,
                    UserId = user.Id
                };
                await dbClient.Insertable(family).ExecuteCommandAsync();

                return GenToken(user.Id, userwx.OpenId, sessionbage.Key);
            }
            else
            {
                await dbClient.Updateable<T_RegUser>().SetColumns(x => new T_RegUser
                {
                    Name = data.nickName,
                    HeadImg = data.avatarUrl
                }).Where(x => x.Id == wxdata.UserId).ExecuteCommandAsync();
                return GenToken(wxdata.UserId, sessionbage.OpenId, sessionbage.Key);
            }
        }

        /// <summary>
        /// 解密用户资料,如果未注册则自动进行注册,否则更新资料
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public async Task<ResultInfo> DecryptDataAsync(DecryptUserInfoC2SDto data)
        {
            var sessionbage = await SessionContainer.GetSessionAsync(data.sessionId);
            if (sessionbage == null)
            {
                return new ResultInfo(ResultState.FAIL, "登录信息已过期");
            }
            //_loggerService.AddLogger($"sessionkey={sessionbage.SessionKey}");
            //var a = DecodeEncryptedData(sessionbage.SessionKey, data.encryptedData, data.iv);
            //_loggerService.AddLogger($"info2={a}");
            DecodeEntityBase decodedEntity = null;
            DecodedUserInfo userinfo = null;
            userinfo = EncryptHelper.DecodeUserInfoBySessionId(
                       data.sessionId,
                       data.encryptedData, data.iv);
            decodedEntity = userinfo;
            //检验水印
            var checkWartmark = false;
            string appid = data.UA == UAConst.WeiXin ? WxOpenAppId : TouTiaoAppId;
            if (decodedEntity != null)
            {
                checkWartmark = decodedEntity.CheckWatermark(appid);
            }
            if (!checkWartmark)
            {
                return new ResultInfo(ResultState.FAIL, "水印验证不通过");
            }
            //检查此openid是否已绑定用户
            var wxdata = await dbClient.Queryable<T_UserWX>().Where(x => x.OpenId == sessionbage.OpenId)
                .Select(x => new T_UserWX
                {
                    UserId = x.UserId
                })
                .FirstAsync()
                ;
            if (wxdata == null)
            {
                var user = new T_RegUser
                {
                    Id = IDGen.NextID(),
                    CreateTime = DateTime.Now,
                    HeadImg = userinfo.avatarUrl,
                    Name = userinfo.nickName
                };
                await dbClient.Insertable(user).ExecuteCommandAsync();

                var userwx = new T_UserWX
                {
                    Id = IDGen.NextID(),
                    OpenId = sessionbage.OpenId.ToStr(),
                    UserId = user.Id
                };
                await dbClient.Insertable(userwx).ExecuteCommandAsync();

                //增加一个默认家庭成员
                var family = new T_Family
                {
                    Id = user.Id,
                    CreateTime = user.CreateTime,
                    HeadImg = user.HeadImg,
                    Name = user.Name,
                    UserId = user.Id
                };
                await dbClient.Insertable(family).ExecuteCommandAsync();

                return GenToken(user.Id, userwx.OpenId, sessionbage.Key);
            }
            else
            {
                await dbClient.Updateable<T_RegUser>().SetColumns(x => new T_RegUser
                {
                    Name = userinfo.nickName,
                    HeadImg = userinfo.avatarUrl
                }).Where(x => x.Id == wxdata.UserId).ExecuteCommandAsync();
                return GenToken(wxdata.UserId, sessionbage.OpenId, sessionbage.Key);
            }
        }
        /// <summary>
        /// 退出登录
        /// </summary>
        /// <param name="sessionId"></param>
        /// <returns></returns>
        public async Task<ResultInfo> OutLoginAsync(string sessionId)
        {
            var sessionBag = await SessionContainer.GetSessionAsync(sessionId);
            if (sessionBag == null)
            {
                return new ResultInfo(ResultState.FAIL, "sessionId未找到");
            }
            await SessionContainer.RemoveFromCacheAsync(sessionBag.OpenId);
            await dbClient.Deleteable<T_UserWX>().Where(x => x.OpenId == sessionBag.OpenId).ExecuteCommandAsync();
            return new ResultInfo(ResultState.SUCCESS, "退出登录成功");
        }

        /// <summary>
        /// 修改用户资料
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public async Task<ResultInfo> SubmitUserInfoAsync(SumitUserInfoC2SDto data)
        {
            var CurrentUser = AuthUserInfoService.GetUserInfo();
            if (await dbClient.Queryable<T_FamilyData>().AnyAsync(x => x.Id == data.FamilyId))
            {
                await dbClient.Updateable<T_FamilyData>().SetColumns(x => new T_FamilyData
                {
                    Brithday = data.Birthday,
                    Sex = data.Sex,
                    Height = data.Height,
                    Weight = data.Weight
                }).Where(x => x.Id == data.FamilyId).ExecuteCommandAsync();
            }
            else
            {
                await dbClient.Insertable(new T_FamilyData
                {
                    Id = data.FamilyId,
                    Sex = data.Sex,
                    Brithday = data.Birthday,
                    Height = data.Height,
                    LastResultTime = null,
                    Weight = data.Weight
                }).ExecuteCommandAsync();
            }
            return new ResultInfo(ResultState.SUCCESS, "资料修改成功");
        }
    }
}
