using Furion.DependencyInjection;
using Mapster;
using Nirvana.Common;
using Nirvana.Common.ApiBase;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using YBDevice.Body.BodyFatHelper;
using YBDevice.Body.Level;
using YBDevice.CommonService;
using YBDevice.CommonService.DevTypeInfo;
using YBDevice.Entity;
using YBDevice.NApi.Application.Prescription;

namespace YBDevice.NApi.Application.UserInfo
{
    /// <summary>
    /// 儿童信息处理
    /// </summary>
    public class ChildService : BaseService, IChildService, ITransient
    {
        private readonly ISqlSugarRepository<YB_Family> repository;
        private readonly SqlSugarClient dbClient;
        private readonly IFamilyService _familyService;
        private readonly IDeviceTypeService _deviceTypeService;
        private readonly IBodyFatHelperService _bodyFatHelperService;
        private readonly IPrescriptionService _prescriptionService;
        private readonly ILevelService _levelService;
        private readonly ILevelService _abLevelService;
        /// <summary>
        /// 构造
        /// </summary>
        /// <param name="sqlSugarRepository"></param>
        /// <param name="familyService"></param>
        /// <param name="bodyFatHelperService"></param>
        /// <param name="deviceTypeService"></param>
        /// <param name="prescriptionService"></param>
        /// <param name="resolveNamed"></param>
        public ChildService(ISqlSugarRepository<YB_Family> sqlSugarRepository, IFamilyService familyService, IBodyFatHelperService bodyFatHelperService, IDeviceTypeService deviceTypeService, IPrescriptionService prescriptionService, Func<string, ITransient, object> resolveNamed)

        {
            repository = sqlSugarRepository;
            dbClient = repository.Context;
            _familyService = familyService;
            _bodyFatHelperService = bodyFatHelperService;
            _deviceTypeService = deviceTypeService;
            _prescriptionService = prescriptionService;
            _levelService = resolveNamed("LevelService", default) as ILevelService;
            _abLevelService = resolveNamed("AbLevelService", default) as ILevelService;
        }

        /// <summary>
        /// 获取儿童信息
        /// </summary>
        /// <param name="familyid"></param>
        /// <param name="devtype">设备类型</param>
        /// <param name="appid">小程序appid</param>
        /// <returns></returns>
        public async Task<ResultInfo> GetInfoAsync(int familyid, int devtype, string appid)
        {
            var data = new ChildInfoModel();
            var family = await dbClient.Queryable<YB_Family>().Where(x => x.Id == familyid).FirstAsync();
            if (family == null)
            {
                return new ResultInfo(ResultState.FAIL, "家庭成员未找到");
            }
            YB_FamilyData familydata = await dbClient.Queryable<YB_FamilyData>().Where(x => x.FamilyId == family.Id).FirstAsync();
            YB_FamilyRealData familyrealdata = await dbClient.Queryable<YB_FamilyRealData>().Where(x => x.FamilyId == family.Id && x.DevType == devtype).FirstAsync();
            decimal height = (familyrealdata != null && familyrealdata.LastHeight > 0) ? familyrealdata.LastHeight : family.Height;
            decimal weight = (familyrealdata != null && familyrealdata.LastWeight > 0) ? familyrealdata.LastWeight.ToDecimal(1) : 0;
            decimal head = (familyrealdata != null && familyrealdata.LastHead > 0) ? familyrealdata.LastHead.ToDecimal(1) : 0;
            int age = family.Birthday.ToAge();
            int month = family.Birthday.ToMonth();
            //如果有测量数据,以最后一次测量时间为准
            if (familyrealdata != null && familyrealdata.LastResultTime.HasValue)
            {
                month = family.Birthday.ToMonth(familyrealdata.LastResultTime.Value);
            }
            data = new ChildInfoModel
            {
                Height = height,
                Head = head,
                DadHeight = familydata != null ? familydata.DadHeight : 0,
                Time = familyrealdata != null ? familyrealdata.LastResultTime.ToYearDateTimes() : "",
                Weight = weight,
                MomHeight = familydata != null ? familydata.MomHeight : 0,
                HalfYearHeight = familydata != null ? (height - familydata.HalfYearHeight) : 0,
                YearHeight = familydata != null ? (height - familydata.YearHeight) : 0,
                Name = family.Name,
                HeadImg = !string.IsNullOrEmpty(family.HeadImg) ? family.HeadImg : DefaultService.HeadImg(family.Sex, family.Type),
                Age = family.Birthday.TomAge(),
                Sex = family.Sex,
                Birthday = family.Birthday.ToString("yyyy-MM-dd"),
                Id = family.Id,
                LastHeightTime = familyrealdata != null ? familyrealdata.LastResultHeightTime.ToYearDate() : "",
                BMI = _bodyFatHelperService.CalcBMi(height, weight),
                Type = family.Type,
                ResultId = familyrealdata != null ? familyrealdata.LastResultId : null,
                ReferList = GetReferList(),
                DataReferList = GetDataReferList(family.Birthday)
            };
            var standdata = await _levelService.LevelAsync(family.Sex, family.Birthday, data.BMI, LevelType.BMI);
            data.BMILevel = standdata.Level;
            data.BMILevelColor = standdata.Color;
            data.BMILevelList = standdata.list;
            //如果是安邦小程序则使用安邦标准
            if (appid == ABConst.MiniAppId)
            {
                standdata = await _abLevelService.LevelAsync(family.Sex, family.Birthday, data.Height, LevelType.Height);
                data.HeightLevel = standdata.Level;
                data.HeightLevelColor = standdata.Color;
                data.HeightLevelList = standdata.list;
                data.PredictHeight = age <= 16 ? standdata.Media : data.Height;
                standdata = await _abLevelService.LevelAsync(family.Sex, family.Birthday, data.Weight, LevelType.Weight);
                data.WeightLevel = standdata.Level;
                data.WeightLevelColor = standdata.Color;
                data.WeightLevelList = standdata.list;
                data.StandWeight = age <= 16 ? standdata.Media : _bodyFatHelperService.CalcStandWeight(height, family.Sex);
            }
            else
            {
                standdata = await _levelService.LevelAsync(family.Sex, family.Birthday, data.Height, LevelType.Height);
                data.HeightLevel = standdata.Level;
                data.HeightLevelColor = standdata.Color;
                data.HeightLevelList = standdata.list;
                data.PredictHeight = age <= 16 ? standdata.Media : data.Height;
                standdata = await _levelService.LevelAsync(family.Sex, family.Birthday, data.Weight, LevelType.Weight);
                data.WeightLevel = standdata.Level;
                data.WeightLevelColor = standdata.Color;
                data.WeightLevelList = standdata.list;
                data.StandWeight = age <= 16 ? standdata.Media : _bodyFatHelperService.CalcStandWeight(height, family.Sex);
            }

            standdata = await _levelService.LevelAsync(family.Sex, family.Birthday, data.Head, LevelType.Head);
            data.HeadLevel = standdata.Level;
            data.HeadLevelColor = standdata.Color;
            data.HeadLevelList = standdata.list;

            data.IsHeight = data.PredictHeight > data.Height;
            data.AlertType = AletType(data.Height, data.PredictHeight);
            data.GeneticHeight = AdultHeight(data.DadHeight, data.MomHeight, family.Sex);
            if (appid == ABConst.MiniAppId)
            {
                data.cplist = await _prescriptionService.AB_Child(new ChildPrescriptionS2SDto
                {
                    Age = month,
                    BMILevel = data.BMILevel,
                    HeightLevel = data.HeightLevel,
                    WeightLevel = data.WeightLevel
                });
            }
            else
            {
                data.cplist = await _prescriptionService.Child(new ChildPrescriptionS2SDto
                {
                    Age = month,
                    BMILevel = data.BMILevel,
                    HeightLevel = data.HeightLevel,
                    WeightLevel = data.WeightLevel
                });
            }
            return new ResultInfo(ResultState.SUCCESS, "success", data);
        }
        /// <summary>
        /// 获取文献引用列表
        /// </summary>
        /// <returns></returns>
        private List<string> GetReferList()
        {
            var list = new List<string> {
                "[1]华东师范大学专业硕士学位论文——跳跃运动对小学生身高和体质健康影响的实验研究",
                "[2]李之俊.运动与脂肪动员研究进展[J].中国运动医学杂志，2012.01.（01）：69-72",
                "[3]曾捷.跳跃运动队青少年根骨骨密度与体质影响的实验研究[D].硕士论文华东师范大学，2013.10:16-18.",
                "[4]牟其林.跳跃运动对贵州小学生骨密度、体质健康和体成分影响的实验研究[D].硕士论文.华东师范大学.2016.10.",
                "[5]张玲莉,闫晓,邹军.运动训练与骨生长代谢的研究进展[J].中国骨质疏松杂志，2013,19(7):761-765."
            };
            return list;
        }
        /// <summary>
        /// 获取数据参考列表
        /// </summary>
        /// <param name="birthday">出生年月</param>
        /// <returns></returns>
        private List<string> GetDataReferList(DateTime birthday)
        {
            var list = new List<string> {
                "《2005年九省/市儿童体格发育调查数据研究》中华儿科杂志，2009年7期"
            };
            double month = birthday.ToDMonth();
            if (month >= 5 * 12 * 1.0 && month <= 19 * 12 * 1.0)
            {
                list.Add("《WHO 5~19岁身高/体重判定标准》");
            }
            return list;
        }

        /// <summary>
        /// 获取成长曲线
        /// </summary>
        /// <param name="model">家庭成员ID</param>
        /// <returns></returns>
        public async Task<ResultInfo> GetGrowthCurveAsync(ChildGrowthQueryModel model)
        {
            DateTime nowtime = DateTime.Now.Date;
            if (model.StartTime.HasValue)
            {
                DateTime time = model.StartTime.Value.Date;
                model.StartTime = time.AddDays(-time.Day).AddDays(1);
            }
            else
            {
                DateTime monthtime = nowtime.AddDays(-nowtime.Day).AddDays(1);//月初
                model.StartTime = monthtime.AddYears(-2);//两年前
            }
            model.EndTime = model.EndTime.Date;
            //如果时间段包含今天则加载今日数据
            List<int> devtypes = await _deviceTypeService.GetDevTypesAsync(model.DevType);
            List<YB_FamilyReportData> glist = new List<YB_FamilyReportData>();
            if (nowtime >= model.StartTime && nowtime <= model.EndTime)
            {
                var familyrealdatalist = await dbClient.Queryable<YB_FamilyRealData>().Where(x => x.FamilyId == model.familyid && x.DevType == model.DevType).FirstAsync();
                if (familyrealdatalist != null && (familyrealdatalist.MonthWeight > 0 || familyrealdatalist.MonthHeight > 0))
                {
                    glist.Add(new YB_FamilyReportData
                    {
                        Head = familyrealdatalist.MonthHead,
                        Weight = familyrealdatalist.MonthWeight,
                        Height = familyrealdatalist.MonthHeight,
                        CreateTime = nowtime
                    });
                }
            }

            var query = await dbClient.Queryable<YB_FamilyReportData>().Where(x => x.FamilyId == model.familyid && x.CreateTime >= model.StartTime && x.CreateTime <= model.EndTime && devtypes.Contains(x.DevType)).OrderBy(x => x.CreateTime, OrderByType.Desc)
                .GroupBy(x => new
                {
                    x.CreateTime
                })
                .Select(x => new YB_FamilyReportData
                {
                    CreateTime = x.CreateTime,
                    Height = SqlFunc.AggregateMax(x.Height),
                    Weight = SqlFunc.AggregateMax(x.Weight),
                    Head = SqlFunc.AggregateMax(x.Head)
                })
            .ToListAsync();
            glist.AddRange(query);
            var list = glist.Adapt<List<ChildGrowthModel>>();
            return new ResultInfo(ResultState.SUCCESS, "success", list);
        }

        /// <summary>
        /// 获取身高/体重成长测评报告,与标准身高进行对比
        /// </summary>
        /// <param name="familyid"></param>
        /// <param name="devtype">设备类型</param>
        /// <returns></returns>
        public async Task<ResultInfo> GetHWListAsync(int familyid, int devtype)
        {
            var family = await dbClient.Queryable<YB_Family>().FirstAsync(x => x.Id == familyid);
            if (family == null)
            {
                return new ResultInfo(ResultState.FAIL, "家庭成员未找到");
            }
            //如果时间段包含今天则加载今日数据
            List<int> devtypes = await _deviceTypeService.GetDevTypesAsync(devtype);
            List<YB_FamilyReportData> glist = new List<YB_FamilyReportData>();
            DateTime nowtime = DateTime.Now.Date;
            DateTime starttime = nowtime.AddDays(-nowtime.Day).AddDays(1);//月初
            DateTime endtime = starttime.AddYears(-1);
            var familyrealdatalist = await dbClient.Queryable<YB_FamilyRealData>().Where(x => x.FamilyId == familyid && x.DevType == devtype).FirstAsync();
            if (familyrealdatalist != null && (familyrealdatalist.MonthHeight > 0 || familyrealdatalist.MonthWeight > 0))
            {
                glist.Add(new YB_FamilyReportData
                {
                    Head = familyrealdatalist.MonthHead,
                    Weight = familyrealdatalist.MonthWeight,
                    Height = familyrealdatalist.MonthHeight,
                    CreateTime = nowtime
                });
            }
            var tempquery = dbClient.Queryable<YB_FamilyReportData>()
                .Where(x => x.FamilyId == familyid && x.CreateTime < starttime && x.CreateTime >= endtime && x.DevType == devtype);
            var query = await tempquery
                .OrderBy(x => x.CreateTime, OrderByType.Desc)
                .ToListAsync();
            glist.AddRange(query);
            var hlist = new List<ChildWHModel>();
            var wlist = new List<ChildWHModel>();
            var headlist = new List<ChildWHModel>();
            //如果年龄过大,则标准体重使用算法
            int age = family.Birthday.ToAge();
            if (age > 16)
            {
                glist.ForEach(x =>
                {
                    hlist.Add(new ChildWHModel
                    {
                        Time = x.CreateTime.ToYearMonth(),
                        Value = x.Height,
                        StandValue = x.Height
                    });
                    wlist.Add(new ChildWHModel
                    {
                        Time = x.CreateTime.ToYearMonth(),
                        Value = x.Weight.ToDecimal(1),
                        StandValue = _bodyFatHelperService.CalcStandWeight(x.Height, (GenderType)family.Sex)
                    });
                    headlist.Add(new ChildWHModel
                    {
                        Time = x.CreateTime.ToYearMonth(),
                        Value = x.Head.ToDecimal(1),
                        StandValue = 0
                    });
                });
            }
            else
            {
                var sheightlist = await dbClient.Queryable<YB_NewHeightStand>().Where(x => x.Sex == family.Sex).OrderBy(x => x.Month, OrderByType.Asc).ToListAsync();
                var sweightlist = await dbClient.Queryable<YB_NewWeightStand>().Where(x => x.Sex == family.Sex).OrderBy(x => x.Month, OrderByType.Asc).ToListAsync();
                var sheadlist = await dbClient.Queryable<YB_HeadStand>().Where(x => x.Sex == family.Sex).OrderBy(x => x.Month, OrderByType.Asc).ToListAsync();
                glist.ForEach(x =>
                {

                    int month = family.Birthday.ToMonth(x.CreateTime);
                    var standheight = sheightlist.Where(x => x.Month >= month).FirstOrDefault();
                    var standweight = sweightlist.Where(x => x.Month >= month).FirstOrDefault();
                    var standhead = sheadlist.Where(x => x.Month >= month).FirstOrDefault();
                    hlist.Add(new ChildWHModel
                    {
                        Time = x.CreateTime.ToYearMonth(),
                        Value = x.Height,
                        StandValue = standheight != null ? standheight.median.ToDecimal(1) : 0
                    });
                    wlist.Add(new ChildWHModel
                    {
                        Time = x.CreateTime.ToYearMonth(),
                        Value = x.Weight.ToDecimal(1),
                        StandValue = standweight != null ? standweight.median.ToDecimal(1) : 0
                    });
                    headlist.Add(new ChildWHModel
                    {
                        Time = x.CreateTime.ToYearMonth(),
                        Value = x.Head.ToDecimal(1),
                        StandValue = standhead != null ? standhead.median.ToDecimal(1) : 0
                    });
                });
            }

            return new ResultInfo(ResultState.SUCCESS, "success", new
            {
                hlist = hlist,
                wlist = wlist,
                headlist = headlist
            });
        }

        /// <summary>
        /// 计算遗传身高和成年身高
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public async Task<ResultInfo> PredictHeightAsync(ChildPredictHeightModel model)
        {
            var standdata = new LevelS2SDto();
            //遗传/标准身高
            decimal geneticheight = 0;
            //成年身高预测
            decimal adultheight = 0;
            //如果是安邦小程序则使用安邦标准
            if (model.AppId == ABConst.MiniAppId)
            {
                standdata = await _abLevelService.LevelAsync(model.sex, model.Birthday, model.Height, LevelType.Height);
                geneticheight = standdata.Media;
                adultheight = AbAdultHeight(model.DadHeight, model.MomHeight, model.sex);
            }
            else
            {
                standdata = await _levelService.LevelAsync(model.sex, model.Birthday, model.Height, LevelType.Height);
                geneticheight = standdata.Media;
                adultheight = AdultHeight(model.DadHeight, model.MomHeight, model.sex);
            }
            if (model.familyid > 0)
            {
                await dbClient.Updateable<YB_FamilyData>().SetColumns(x => new YB_FamilyData
                {
                    DadHeight = model.DadHeight,
                    MomHeight = model.MomHeight
                })
                    .Where(x => x.FamilyId == model.familyid)
                    .ExecuteCommandAsync();
            }
            return new ResultInfo(ResultState.SUCCESS, "success", new ChildPredictHeightReturnModel
            {
                GeneticHeight = geneticheight,
                AdultHeight = adultheight,
                ErrorValue = model.sex == GenderType.Male ? 7.5m : 6m
            });
        }

        /// <summary>
        /// 获取儿童增量信息
        /// </summary>
        /// <param name="familyid"></param>
        /// <param name="devtype">设备类型</param>
        /// <param name="appid">小程序appid</param>
        /// <returns></returns>
        public async Task<ResultInfo> GetYearHeightInfoAsync(int familyid, int devtype, string appid)
        {
            var family = await dbClient.Queryable<YB_Family>().FirstAsync(x => x.Id == familyid);
            if (family == null)
            {
                return new ResultInfo(ResultState.FAIL, "成员未找到");
            }
            var familydata = await dbClient.Queryable<YB_FamilyRealData>().FirstAsync(x => x.FamilyId == familyid && x.DevType == devtype);
            int month = family.Birthday.ToMonth();
            int age = family.Birthday.ToAge();
            LevelS2SDto standdata = new LevelS2SDto();
            decimal height = (familydata != null && familydata.LastHeight > 0) ? familydata.LastHeight : family.Height;
            //如果是安邦小程序则使用安邦标准
            if (appid == ABConst.MiniAppId)
            {
                standdata = await _abLevelService.LevelAsync(family.Sex, family.Birthday, height, LevelType.Height);
            }
            else
            {
                standdata = await _levelService.LevelAsync(family.Sex, family.Birthday, height, LevelType.Height);
            }
            var returndata = new ChildYearHeightModel
            {
                f1sd = standdata.Data != null ? standdata.Data.f1sd : 0,
                f2sd = standdata.Data != null ? standdata.Data.f2sd : 0,
                f3sd = standdata.Data != null ? standdata.Data.f3sd : 0,
                z1sd = standdata.Data != null ? standdata.Data.z1sd : 0,
                z2sd = standdata.Data != null ? standdata.Data.z2sd : 0,
                z3sd = standdata.Data != null ? standdata.Data.z3sd : 0,
                Height = height,
                HalfYearHeight = familydata != null && familydata.HalfYearHeight > 0 ? familydata.LastHeight - familydata.HalfYearHeight : 0, //如果半年前身高为0则表示未测量过,则增量为0
                median = standdata.Data != null ? standdata.Data.median : 0,
                HalfYearStandHeight = HalfYearStandHeight(age),
                YearHeight = familydata != null && familydata.YearHeight > 0 ? familydata.LastHeight - familydata.YearHeight : 0,
                YearStandHeight = YearStandHeight(age),
                HeightLevel = standdata != null ? standdata.HeightLevel : ChildHeightLevel.Error,
                list = standdata.list
            };
            returndata.HalfYearHeightLevel = HalfYearHeightLevel(returndata.HalfYearHeight, returndata.HalfYearStandHeight);
            returndata.YearHeightLevel = YearHeightLevel(returndata.YearHeight, returndata.YearStandHeight);
            return new ResultInfo(ResultState.SUCCESS, "success", returndata);
        }

        /// <summary>
        /// 半年增量标准身高值
        /// </summary>
        /// <param name="age">年龄</param>
        /// <returns></returns>
        private decimal HalfYearStandHeight(int age)
            => (age) switch
            {
                _ when age < 10 => 2.5m,
                _ => 3m
            };
        /// <summary>
        /// 半年增量标准
        /// </summary>
        /// <param name="height">半年增量</param>
        /// <param name="standheight">标准增量</param>
        /// <returns></returns>
        private int HalfYearHeightLevel(decimal height, decimal standheight)
            => (height, standheight) switch
            {
                _ when height >= standheight => (int)ChildYearHeightLevel.Normal,
                _ => (int)ChildHeightLevel.Low
            };
        /// <summary>
        /// 一年的身高增量标准值
        /// </summary>
        /// <param name="age"></param>
        /// <returns></returns>
        private decimal YearStandHeight(int age)
          => (age) switch
          {
              _ when age < 10 => 5m,
              _ => 6m
          };
        /// <summary>
        /// 一年增量标准
        /// </summary>
        /// <param name="height">一年增量</param>
        /// <param name="standheight">标准增量</param>
        /// <returns></returns>
        private int YearHeightLevel(decimal height, decimal standheight)
            => (height, standheight) switch
            {
                _ when height >= standheight => (int)ChildYearHeightLevel.Normal,
                _ => (int)ChildHeightLevel.Low
            };
        /// <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>
        /// 安邦成年身高预测,男孩=（爸爸+妈妈+13）➗2，，最终结果±7.5
        /// 女孩：(爸爸+妈妈-13）➗2，，最终结果±6
        /// </summary>
        /// <param name="dadheight"></param>
        /// <param name="momheight"></param>
        /// <param name="sex"></param>
        /// <returns></returns>
        private static decimal AbAdultHeight(decimal dadheight, decimal momheight, GenderType sex)
           => (dadheight, momheight, sex) switch
           {
               _ when dadheight <= 0 || momheight <= 0 => 0,
               _ when (sex == GenderType.FeMale) => (dadheight + momheight - 13) / 2.ToDecimal(1),
               _ => (dadheight + momheight + 13) / 2.ToDecimal(1)
           };
        /// <summary>
        /// 智能追踪分析,该分析是基于标准身高与实测身高的对比，对孩子是否因为后天因素有利于长高进行判断
        /// </summary>
        /// <param name="height">实测身高</param>
        /// <param name="standheight">标准身高</param>
        /// <returns></returns>
        private static int AletType(decimal height, decimal standheight)
            => (height, standheight) switch
            {
                _ when (standheight - height > 6) => (int)ChildAlertType.RedAlert,
                _ when (standheight - height <= 6 && standheight - height > 3) => (int)ChildAlertType.OrangeAlert,
                _ when (standheight - height <= 3 && standheight - height > 0) => (int)ChildAlertType.YellowAlert,
                _ when (standheight - height <= 0 && standheight - height >= -4) => (int)ChildAlertType.BlueReward,
                _ => (int)ChildAlertType.GreenAlert
            };
    }
}
