using Nirvana.Common;
using Nirvana.Common.ApiBase;
using Nirvana.Data;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using YBDevice.Entity;

namespace YBDevice.NApi.DBServices
{
    /// <summary>
    /// 儿童模式相关处理
    /// </summary>
    public partial class ChildApp : BaseApp
    {
        /// <summary>
        /// 获取儿童信息
        /// </summary>
        /// <param name="familyid"></param>
        /// <returns></returns>
        public async Task<ResultInfo> GetInfoAsync(int familyid)
        {
            //如果familyid=0,则加载演示数据
            var data = new ChildInfoModel();
            using (var dbClient = ReadDbContext.GetInstance())
            {
                //如果传的成人类型,则自动寻找一个最新的
                var family = await dbClient.Queryable<YB_Family>().Where(x => x.Id == familyid && x.Type == 2 && x.Status != -1).FirstAsync();
                if (family == null)
                {
                    family = await dbClient.Queryable<YB_Family>().Where(x => x.UserId == authInfo.UserId && x.Type == 2 && x.Status != -1).OrderBy(x => x.LastHeartTime, OrderByType.Desc).FirstAsync();
                }
                if (family == null)
                {
                    var ysfamily = new YSFamily();
                    //返回一条演示数据
                    data = new ChildInfoModel
                    {
                        IsTest = true,
                        Height = ysfamily.Height,
                        DadHeight = ysfamily.DadHeight,
                        Time = ysfamily.Time,
                        Weight = ysfamily.Weight,
                        MomHeight = ysfamily.MomHeight,
                        Birthday = ysfamily.Birthday.ToString("yyyy-MM-dd"),
                        PredictHeight = await StandHeightAsync(ysfamily.Sex, ysfamily.Month),
                        HalfYearHeight = ysfamily.HalfYearHeight,
                        YearHeight = ysfamily.YearHeight,
                        Name = ysfamily.Name,
                        HeadImg = ysfamily.HeadImg,
                        Age = ysfamily.mAge,
                        Sex = ysfamily.Sex,
                        Id = ysfamily.Id
                    };
                    data.IsHeight = data.PredictHeight > data.Height;
                    data.AlertType = AletType(data.Height, data.PredictHeight);
                    data.GeneticHeight = AdultHeight(data.DadHeight, data.MomHeight, data.Sex);
                    return new ResultInfo(ResultState.SUCCESS, "success", data);
                    //return new ResultInfo(ResultState.FAIL, "成员未找到");
                }
                var familydata = await dbClient.Queryable<YB_FamilyData>().Where(x => x.FamilyId == family.Id).FirstAsync();
                int month = family.Birthday.ToMonth();
                data = new ChildInfoModel
                {
                    Height = family.Height,
                    DadHeight = familydata != null ? familydata.DadHeight : 0,
                    Time = family.LastHeartTime,
                    Weight = family.Weight,
                    MomHeight = familydata != null ? familydata.MomHeight : 0,
                    PredictHeight = await StandHeightAsync(family.Sex, month),
                    HalfYearHeight = familydata != null ? family.Height - familydata.HalfYearHeight : 0,
                    YearHeight = familydata != null ? family.Height - familydata.YearHeight : 0,
                    Name = family.Name,
                    HeadImg = !string.IsNullOrEmpty(family.HeadImg) ? family.HeadImg : new FamilyApp().HeadImg(family.Sex, family.Type),
                    Age = family.Birthday.TomAge(),
                    Sex = family.Sex,
                    Birthday = family.Birthday.ToString("yyyy-MM-dd"),
                    Id = family.Id
                };
                data.IsHeight = data.PredictHeight > data.Height;
                data.AlertType = AletType(data.Height, data.PredictHeight);
                data.GeneticHeight = AdultHeight(data.DadHeight, data.MomHeight, family.Sex);
                return new ResultInfo(ResultState.SUCCESS, "success", data);
            }
        }
        /// <summary>
        /// 获取成长曲线
        /// </summary>
        /// <param name="model">家庭成员ID</param>
        /// <returns></returns>
        public async Task<ResultInfo> GetGrowthCurveAsync(ChildGrowthQueryModel model)
        {
            //如果familyid为0,则加载演示数据
            if (model.familyid <= 0)
            {
                var list = new List<ChildGrowthModel>();
                for (var i = 0; i < 10; i++)
                {
                    list.Add(new ChildGrowthModel
                    {
                        Time = $"{i + 1}",
                        Height = 100 + i * 2,
                        Weight = 20 + i
                    });
                }
                return new ResultInfo(ResultState.SUCCESS, "success", list);
            }
            using (var dbClient = ReadDbContext.GetInstance())
            {
                model.StartTime = model.StartTime.Date;
                model.EndTime = model.EndTime.Date;
                var tempquery = dbClient.Queryable<YB_FamilyReportData>()
                    .Where(x => x.FamilyId == model.familyid && x.CreateTime>=model.StartTime &&  x.CreateTime<=model.EndTime);
                var query = await tempquery.OrderBy(x => x.CreateTime, OrderByType.Desc).Select(x => new ChildGrowthModel
                {
                    Time = SqlFunc.ToString(x.CreateTime),
                    Height = x.Height,
                    Weight = x.Weight
                })
                    .Mapper((it, cache) =>
                    {
                        it.Time = it.Time.ToDate().ToString("yyyy-MM");
                    })
                    .ToPageListAsync(model.page, model.pagesize);
                return new ResultInfo(ResultState.SUCCESS, "success", query);
            }
        }

        /// <summary>
        /// 获取身高/体重成长测评报告,与标准身高进行对比
        /// </summary>
        /// <param name="familyid"></param>
        /// <param name="page"></param>
        /// <param name="pagesize"></param>
        /// <returns></returns>
        public async Task<ResultInfo> GetHWListAsync(int familyid, int page, int pagesize)
        {
            if (familyid <= 0)
            {
                var thlist = new List<ChildWHModel>();
                var twlist = new List<ChildWHModel>();
                for (var i = 0; i < 10; i++)
                {
                    thlist.Add(new ChildWHModel
                    {
                        Time = $"{i + 1}",
                        Value = 100 + i * 2,
                        StandValue = await StandHeightAsync(1, 84)
                    });
                    twlist.Add(new ChildWHModel
                    {
                        Time = $"{i + 1}",
                        Value = 20 + i * 2,
                        StandValue = await StandWeightAsync(1, 84)
                    });
                }
                return new ResultInfo(ResultState.SUCCESS, "success", new
                {
                    hlist = thlist,
                    wlist = twlist
                });
            }
            using (var dbClient = ReadDbContext.GetInstance())
            {
                var family = await dbClient.Queryable<YB_Family>().FirstAsync(x => x.Id == familyid);
                if (family == null)
                {
                    return new ResultInfo(ResultState.FAIL, "家庭成员未找到");
                }
                var tempquery = dbClient.Queryable<YB_FamilyReportData>().Where(x => x.FamilyId == familyid);
                var query = await tempquery
                    .OrderBy(x => x.CreateTime, OrderByType.Desc)
                    .ToPageListAsync(page, pagesize);
                var hlist = new List<ChildWHModel>();
                var wlist = new List<ChildWHModel>();
                query.ForEach(async x =>
                {
                    hlist.Add(new ChildWHModel
                    {
                        Time = x.CreateTime.ToYearDate(),
                        Value = x.Height,
                        StandValue = await StandHeightAsync(family.Sex, family.Birthday)
                    });
                    wlist.Add(new ChildWHModel
                    {
                        Time = x.CreateTime.ToYearDate(),
                        Value = x.Weight,
                        StandValue = await StandWeightAsync(family.Sex, family.Birthday)
                    }); ;
                });
                return new ResultInfo(ResultState.SUCCESS, "success", new
                {
                    hlist = hlist,
                    wlist = wlist
                });
            }
        }

        /// <summary>
        /// 计算遗传身高和成年身高
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public async Task<ResultInfo> PredictHeightAsync(ChildPredictHeightModel model)
        {
            //计算遗传身高
            decimal geneticheight = await StandHeightAsync(model.sex, model.Birthday);
            //成年身高预测
            decimal adultheight = AdultHeight(model.DadHeight, model.MomHeight, model.sex);
            if (model.familyid > 0)
            {
                using (var dbClient = ReadDbContext.GetInstance())
                {
                    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
            });
        }
        /// <summary>
        /// 获取儿童增量信息
        /// </summary>
        /// <param name="familyid"></param>
        /// <returns></returns>
        public async Task<ResultInfo> GetYearHeightInfoAsync(int familyid)
        {
            using (var dbClient = ReadDbContext.GetInstance())
            {
                if(familyid == 0)
                {
                    //返回演示数据
                    var ysfamily = new YSFamily();
                    var ysstanddata = await dbClient.Queryable<YB_HeightStand>().Where(x => x.Sex == ysfamily.Sex && x.Month >= ysfamily.Month).OrderBy(x => x.Month, OrderByType.Asc).FirstAsync(); 
                    var ysdata = new ChildYearHeightModel { 
                        f1sd= ysstanddata.f1sd,
                        f2sd=ysstanddata.f2sd,
                        z1sd=ysstanddata.z1sd,
                        z2sd=ysstanddata.z2sd,
                        Height=ysfamily.Height,
                        HalfYearHeight=ysfamily.HalfYearHeight,
                        median=ysstanddata.median,
                        HalfYearStandHeight=HalfYearStandHeight(ysfamily.Age),
                        YearHeight=ysfamily.YearHeight,
                        HeightLevel=HeightLevel(ysstanddata,ysfamily.Height)
                    };
                    ysdata.HalfYearHeightLevel = HalfYearHeightLevel(ysdata.HalfYearHeight, ysdata.HalfYearStandHeight);
                    ysdata.YearHeightLevel = YearHeightLevel(ysdata.YearHeight, ysdata.YearStandHeight);
                    return new ResultInfo(ResultState.SUCCESS, "success", ysdata);
                }
                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_FamilyData>().FirstAsync(x => x.FamilyId == familyid);
                int month = family.Birthday.ToMonth();
                int age = family.Birthday.ToAge();
                var data = await dbClient.Queryable<YB_HeightStand>().Where(x => x.Sex == family.Sex && (x.Month >= month)).OrderBy(x => x.Month, OrderByType.Asc).FirstAsync();
                var returndata = new ChildYearHeightModel
                {
                    f1sd = data.f1sd,
                    f2sd = data.f2sd,
                    z1sd = data.z1sd,
                    z2sd = data.z2sd,
                    Height = family.Height,
                    HalfYearHeight = familydata != null && familydata.HalfYearHeight > 0 ? family.Height - familydata.HalfYearHeight : 0, //如果半年前身高为0则表示未测量过,则增量为0
                    median = data.median,
                    HalfYearStandHeight = HalfYearStandHeight(age),
                    YearHeight = familydata != null && familydata.YearHeight > 0 ? family.Height - familydata.YearHeight : 0,
                    YearStandHeight = YearStandHeight(age),
                    HeightLevel = HeightLevel(data, family.Height)
                };
                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="data">等级标准</param>
        /// <param name="height">身高</param>
        /// <returns></returns>
        private int HeightLevel(YB_HeightStand data, decimal height)
            => (data, height) switch
            {
                _ when height >= data.f1sd && height < data.z1sd => (int)ChildHeightLevel.Normal,
                _ when height >= data.f2sd && height < data.f1sd => (int)ChildHeightLevel.Low,
                _ when height >= data.z1sd && height < data.z2sd => (int)ChildHeightLevel.Height,
                _ when height >= data.f3sd && height < data.f2sd => (int)ChildHeightLevel.MoreLow,
                _ when height >= data.z2sd && height < data.z3sd => (int)ChildHeightLevel.MoreHeight,
                _ => (int)ChildHeightLevel.Error
            };

        /// <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, int sex)
            => (dadheight, momheight, sex) switch
            {
                _ when dadheight <= 0 || momheight <= 0 => 0,
                _ when (sex == 2) => (37.85m + 0.75m * (dadheight + momheight) / 2).ToDecimal(1),
                _ => (45.99m + 0.78m * (dadheight + momheight) / 2).ToDecimal(1)
            };
        /// <summary>
        /// 标准身高,参考
        /// 5-19岁女孩:https://www.who.int/docs/default-source/child-growth/growth-reference-5-19-years/height-for-age-(5-19-years)/sft-hfa-girls-perc-5-19years.pdf?sfvrsn=59b013d8_4,
        /// 5-19岁男孩:https://www.who.int/docs/default-source/child-growth/growth-reference-5-19-years/height-for-age-(5-19-years)/sft-hfa-boys-perc-5-19years.pdf?sfvrsn=3fe316bf_4
        /// 
        /// </summary>
        /// <param name="sex"></param>
        /// <param name="birthday"></param>
        /// <returns></returns>
        private static async Task<decimal> StandHeightAsync(int sex, DateTime birthday)
        {
            var month = birthday.ToMonth();
            var height= await StandHeightAsync(sex, month);
            return height.ToDecimal(1);
        }
        /// <summary>
        /// 标准身高
        /// </summary>
        /// <param name="sex">性别,1-男,2-女</param>
        /// <param name="month">月龄</param>
        /// <returns></returns>
        private static async Task<decimal> StandHeightAsync(int sex, int month)
        {
            using (var dbClient = ReadDbContext.GetInstance())
            {
                var data = await dbClient.Queryable<YB_HeightStand>().Where(x => x.Sex == sex && (x.Month >= month)).OrderBy(x => x.Month, OrderByType.Asc).FirstAsync();
                return data != null ? data.median : 0;
            }
        }

        /// <summary>
        /// 标准身高,参考
        /// 5-19岁女孩:https://www.who.int/docs/default-source/child-growth/growth-reference-5-19-years/height-for-age-(5-19-years)/sft-hfa-girls-perc-5-19years.pdf?sfvrsn=59b013d8_4,
        /// 5-19岁男孩:https://www.who.int/docs/default-source/child-growth/growth-reference-5-19-years/height-for-age-(5-19-years)/sft-hfa-boys-perc-5-19years.pdf?sfvrsn=3fe316bf_4
        /// 
        /// </summary>
        /// <param name="sex"></param>
        /// <param name="birthday"></param>
        /// <returns></returns>
        private static async Task<decimal> StandWeightAsync(int sex, DateTime birthday)
        {
            var month = birthday.ToMonth();
            return await StandWeightAsync(sex, month);
        }


        /// <summary>
        /// 标准体重
        /// </summary>
        /// <param name="sex">性别,1-男,2-女</param>
        ///   /// <param name="month">月龄</param>
        /// <returns></returns>
        private static async Task<decimal> StandWeightAsync(int sex, int month)
        {
            using (var dbClient = ReadDbContext.GetInstance())
            {
                var data = await dbClient.Queryable<YB_WeightStand>().Where(x => x.Sex == sex && (x.Month >= month)).OrderBy(x => x.Month, OrderByType.Asc).FirstAsync();
                return data != null ? data.median : 0;
            }

            //理想体重 = 22 ×身高² （米）
            //return 22 * ((height / 100) * (height / 100));
        }
        /// <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
            };
    }
}
