using AutoMapper;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.Extensions.Logging;
using NPOI.SS.Formula.Functions;
using Performance.DtoModels;
using Performance.EntityModels;
using Performance.Repository;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Performance.Services
{
    public class BudgetService : IAutoInjection
    {
        private readonly IMapper _mapper;
        private readonly PerforPerbudgetamountRepository perbudgetamountRepository;
        private readonly PerforPerbudgetratioRepository perbudgetratioRepository;
        private readonly PerforPerbudgetresultRepository perbudgetresultRepository;
        private readonly PerforPerallotRepository perallotRepository;
        private readonly ILogger logger;

        public BudgetService(
            IMapper mapper,
            PerforPerbudgetamountRepository perbudgetamountRepository,
            PerforPerbudgetratioRepository perbudgetratioRepository,
            PerforPerbudgetresultRepository perbudgetresultRepository,
            PerforPerallotRepository perallotRepository,
            ILogger<BudgetService> logger)
        {
            _mapper = mapper;
            this.perbudgetamountRepository = perbudgetamountRepository;
            this.perbudgetratioRepository = perbudgetratioRepository;
            this.perbudgetresultRepository = perbudgetresultRepository;
            this.perallotRepository = perallotRepository;
            this.logger = logger;
        }

        /// <summary>
        /// 预算管理查询（包含金额、占比）
        /// </summary>
        /// <param name="hospitalid"></param>
        /// <param name="year"></param>
        /// <returns></returns>
        public List<BudgetResponse> QueryBudgetByYear(int hospitalid, int year)
        {
            var amounts = perbudgetamountRepository.GetEntities(t => t.HospitalId == hospitalid && t.MainYear == year);
            var ratios = perbudgetratioRepository.GetEntities(t => t.HospitalId == hospitalid && t.MainYear == year);
            var result = _mapper.Map<List<BudgetResponse>>(amounts);
            if (result == null)
                return _mapper.Map<List<BudgetResponse>>(ratios);
            else if (ratios != null && ratios.Any())
            {
                result.AddRange(_mapper.Map<List<BudgetResponse>>(ratios));
            }
            return result.OrderBy(t => t.Year).ToList();
        }

        /// <summary>
        /// 保存预算管理数据
        /// </summary>
        /// <param name="request"></param>
        /// <param name="userId"></param>
        /// <returns></returns>
        public bool SaveBudgetData(int mainYear, List<BudgetResponse> request, int userId)
        {
            if (request == null || !request.Any(t => t.Type == 1) || !request.Any(t => t.Type == 2))
                throw new PerformanceException("提交数据中含无效数据");

            var entity = perbudgetamountRepository.GetEntities(w => w.HospitalId == request.First().HospitalId && w.MainYear == mainYear);
            if (entity != null && entity.Any())
                throw new PerformanceException($"{mainYear}年数据已存在");

            var amounts = _mapper.Map<List<per_budget_amount>>(request.Where(t => t.Type == 1));
            amounts.ForEach(t =>
            {
                t.MainYear = mainYear;
                t.CreateTime = DateTime.Now;
                t.CreateUser = userId;
            });
            if (amounts != null && perbudgetamountRepository.AddRange(amounts.ToArray()))
            {
                var ratios = _mapper.Map<List<per_budget_ratio>>(request.Where(t => t.Type == 2));
                var budgetData = request.FirstOrDefault(t => t.Type == 2);
                ratios.ForEach(t =>
                {
                    t.HospitalRevenue = budgetData?.HospitalRevenue;
                    t.MeritPayIncrement = budgetData?.MeritPayIncrement;
                    t.TheDrugIncomeBudget = budgetData?.TheDrugIncomeBudget;
                    t.MaterialIncomeBudget = budgetData?.MaterialIncomeBudget;
                    t.MainYear = mainYear;
                    t.CreateTime = DateTime.Now;
                    t.CreateUser = userId;
                });
                return perbudgetratioRepository.AddRange(ratios.ToArray());
            }

            return false;
        }

        /// <summary>
        /// 修改预算管理数据
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public bool ModifyBudgetData(List<BudgetResponse> request)
        {
            if (request == null || !request.Any(t => t.Type == 1) || !request.Any(t => t.Type == 2))
                throw new PerformanceException("提交数据中含无效数据");

            var amounts = perbudgetamountRepository.GetEntities(w => request.Where(t => t.Type == 1).Select(t => t.Id).Contains(w.Id));
            if (amounts != null && amounts.Any())
            {
                foreach (var item in amounts)
                {
                    var data = request.FirstOrDefault(t => t.Type == 1 && t.Id == item.Id);
                    if (data == null) continue;
                    item.HospitalRevenue = data.HospitalRevenue;
                    item.HospitalRevenue = data.HospitalRevenue;
                    item.TotalLaborCost = data.TotalLaborCost;
                    item.StaffSpendingFixed = data.StaffSpendingFixed;
                    item.StaffSpendingBonus = data.StaffSpendingBonus;
                    item.TotalMeritPay = data.TotalMeritPay;
                    item.AmountOfStaff = data.AmountOfStaff;
                    item.ThePerCapitaMerit = data.ThePerCapitaMerit;
                    item.TheIncrementCapita = data.TheIncrementCapita;
                    item.TheDrugIncome = data.TheDrugIncome;
                    item.MaterialIncome = data.MaterialIncome;
                    item.TheCostInTotal = data.TheCostInTotal;
                    item.DepreOfFixedAssets = data.DepreOfFixedAssets;
                    item.AmortOfIntangibleAssets = data.AmortOfIntangibleAssets;
                    item.MedicalRisk = data.MedicalRisk;
                    item.OtherExpenses = data.OtherExpenses;
                    item.BalanceOfPayments = data.BalanceOfPayments;
                    item.FiscalSubsidies = data.FiscalSubsidies;
                    item.TheTotalHospitalBalance = data.TheTotalHospitalBalance;
                }
                perbudgetamountRepository.UpdateRange(amounts.ToArray());
            }

            var ratios = perbudgetratioRepository.GetEntities(w => request.Where(t => t.Type == 2).Select(t => t.Id).Contains(w.Id));
            if (ratios != null && ratios.Any())
            {
                foreach (var item in ratios)
                {
                    var data = request.FirstOrDefault(t => t.Type == 2 && t.Id == item.Id);
                    var budgetData = request.FirstOrDefault(t => t.Type == 2);
                    if (data == null) continue;
                    item.HospitalRevenue = data.HospitalRevenue == 0 ? budgetData?.HospitalRevenue:data.HospitalRevenue;
                    item.TotalLaborCost = data.TotalLaborCost;
                    item.StaffSpendingFixed = data.StaffSpendingFixed;
                    item.StaffSpendingBonus = data.StaffSpendingBonus;
                    item.MeritPayIncrement = data.MeritPayIncrement==0?budgetData?.MeritPayIncrement:data.MeritPayIncrement;
                    item.TotalMeritPay = data.TotalMeritPay;
                    item.Amplification = data.Amplification;
                    item.IncrementCapitaRatio = data.IncrementCapitaRatio;
                    item.TheDrugIncome = data.TheDrugIncome;
                    item.TheDrugIncomeBudget = data.TheDrugIncomeBudget==0?budgetData?.TheDrugIncomeBudget:data.TheDrugIncomeBudget;
                    item.MaterialIncome = data.MaterialIncome;
                    item.MaterialIncomeBudget = data.MaterialIncomeBudget==0?budgetData?.MaterialIncomeBudget:data.MaterialIncomeBudget;
                    item.TheCostInTotal = data.TheCostInTotal;
                    item.DepreOfFixedAssets = data.DepreOfFixedAssets;
                    item.AmortOfIntangibleAssets = data.AmortOfIntangibleAssets;
                    item.MedicalRisk = data.MedicalRisk;
                    item.OtherExpenses = data.OtherExpenses;
                    item.BalanceOfPayments = data.BalanceOfPayments;
                    item.TheTotalHospitalBalance = data.TheTotalHospitalBalance;
                }
                perbudgetratioRepository.UpdateRange(ratios.ToArray());
            }

            return true;
        }

        /// <summary>
        /// 预算合计数据查询
        /// </summary>
        /// <param name="hospitalid"></param>
        /// <param name="year"></param>
        /// <returns></returns>
        public List<per_budget_result> QueryResultByYear(int hospitalid, int year)
        {
            var results = perbudgetresultRepository.GetEntities(t => t.HospitalId == hospitalid && t.Year == year);
            return results?.OrderBy(t => t.Month).ToList();
        }

        /// <summary>
        /// 预算比例、历史比例查询
        /// </summary>
        /// <param name="hospitalid"></param>
        /// <param name="year"></param>
        /// <returns></returns>
        public List<BudgetRatioResponse> QueryBudgetRatio(int hospitalid, int year)
        {
            var ratios = perbudgetratioRepository.GetEntities(t => t.HospitalId == hospitalid && t.MainYear == year);

            if (ratios != null && ratios.Any())
            {
                var result = ratios.Select(t => new BudgetRatioResponse
                {
                    HospitalId = t.HospitalId,
                    Year = t.Year,
                    PersonExpendRatio = t.TotalLaborCost,
                    NoPerformanceExpenditureRatio = t.StaffSpendingFixed,
                    NewPerformanceRatio = t.StaffSpendingBonus,
                    MedicinePropRatio = t.TheDrugIncome,
                    MaterialCostsRatio = t.MaterialIncome,
                    TotalMedicineRatio = t.TheDrugIncome + t.MaterialIncome,
                    DepreOfFixedAssetsRatio = t.DepreOfFixedAssets,
                    OtherExpensesRatio = t.OtherExpenses,
                    TheCostInTotalRatio = t.TheCostInTotal,
                    TheCostCombinedRatio = t.DepreOfFixedAssets + t.OtherExpenses + t.TheCostInTotal,
                });

                return result.OrderBy(t => t.Year).ToList();
            }
            return new List<BudgetRatioResponse>();
        }

        /// <summary>
        /// 保存预算合计数据
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public bool SaveBudgetRatio(List<per_budget_result> request, int userId)
        {
            if (request == null || !request.Any())
                throw new PerformanceException("提交数据为空");

            // 有效Id, 已经提交过的数据
            var effectIds = request.Where(t => t.Id != 0).Select(t => t.Id);
            if (effectIds != null && effectIds.Any())
            {
                var results = perbudgetresultRepository.GetEntities(t => effectIds.Contains(t.Id));
                if (results == null || !results.Any())
                    throw new PerformanceException("提交数据中需要修改的数据无效");
                foreach (var item in results)
                {
                    var data = request.FirstOrDefault(t => t.Id == item.Id);
                    if (data == null) continue;

                    item.Grant = data.Grant;
                    item.Adjust = data.Adjust;
                    item.MedicalIncome = data.MedicalIncome;
                    item.PersonnelExpenditure = data.PersonnelExpenditure;
                    item.TheNewPerformance = data.TheNewPerformance;
                    item.MedicineProportion = data.MedicineProportion;
                    item.MaterialCosts = data.MaterialCosts;
                    item.TotalMedicine = data.TotalMedicine;
                    item.DepreOfFixedAssets = data.DepreOfFixedAssets;
                    item.OtherExpenses = data.OtherExpenses;
                    item.TheCostInTotal = data.TheCostInTotal;
                    item.TheCostCombined = data.TheCostCombined;
                    item.PersonExpendRatio = data.PersonExpendRatio;
                    item.NewPerformanceRatio = data.NewPerformanceRatio;
                    item.MedicinePropRatio = data.MedicinePropRatio;
                    item.MaterialCostsRatio = data.MaterialCostsRatio;
                    item.TotalMedicineRatio = data.TotalMedicineRatio;
                    item.DepreOfFixedAssetsRatio = data.DepreOfFixedAssetsRatio;
                    item.OtherExpensesRatio = data.OtherExpensesRatio;
                    item.TheCostInTotalRatio = data.TheCostInTotalRatio;
                    item.TheCostCombinedRatio = data.TheCostCombinedRatio;
                }

                perbudgetresultRepository.UpdateRange(results.ToArray());
            }

            var addData = request.Where(t => t.Id == 0);
            if (addData != null && addData.Any())
            {
                addData.ToList().ForEach(t =>
                {
                    t.CreateDate = DateTime.Now;
                    t.CreateUser = userId;
                    t.States = 1;
                });
                perbudgetresultRepository.AddRange(addData.ToArray());
            }

            return true;
        }

        /// <summary>
        /// 修改发放系数、调节系数使用状态
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public bool UseResult(int id)
        {
            var result = perbudgetresultRepository.GetEntity(t => t.Id == id);
            if (result == null)
                throw new PerformanceException("所选数据不存在!");
            if (result.MedicalIncome == 0 || result.MedicineProportion == 0 || result.TheNewPerformance == 0)
            {
                throw new PerformanceException("请保证医疗收入, 新绩效, 药占比收入等值正确录入 或 使用系数前请先保存填写的数据");
            }
            result.States = 2;
            return perbudgetresultRepository.Update(result);
        }

        /// <summary>
        /// 修改发放系数、调节系数使用状态
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public bool CancelResult(int id)
        {
            var result = perbudgetresultRepository.GetEntity(t => t.Id == id);
            if (result == null)
                throw new PerformanceException("所选数据不存在!");
            result.States = 3;
            return perbudgetresultRepository.Update(result);
        }

        public bool GetAdjustAndGrant(int allotId, out decimal adjust, out decimal grant)
        {
            adjust = 1m; grant = 1m;
            var allot = perallotRepository.GetEntity(t => t.ID == allotId);
            if (allot == null) return false;

            var result = perbudgetresultRepository.GetEntity(t => t.HospitalId == allot.HospitalId && t.Year == allot.Year && t.Month == allot.Month);
            if (result == null || result.States != 2) return false;

            adjust = result.Adjust.Value;
            grant = result.Grant.Value;
            return true;
        }

        public bool GetAdjustAndGrant(per_allot allot, out decimal adjust, out decimal grant)
        {
            logger.LogInformation("获取发放系数、调节系数");
            adjust = 1m; grant = 1m;
            var result = perbudgetresultRepository.GetEntity(t => t.HospitalId == allot.HospitalId && t.Year == allot.Year && t.Month == allot.Month);
            if (result == null || result.States != 2) return false;

            logger.LogInformation("使用预算管理中生成的发放系数、调节系数");
            adjust = result.Adjust.Value / 100;
            grant = result.Grant.Value / 100;
            return true;
        }

        /// <summary>
        /// 统计指定月份绩效信息 
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public per_budget_result Collect(int hospitalid, int year, int month)
        {
            return new per_budget_result
            {
                Year = year,
                Month = month,
                MedicalIncome = GetMedicalIncome(hospitalid, year, month),
                TheNewPerformance = GetTheNewPerformance(hospitalid, year, month),
                MedicineProportion = GetMedicineProportion(hospitalid, year, month),
                MaterialCosts = GetMaterialCosts(hospitalid, year, month),
            };
        }
        ///绩效总金额
        private decimal GetTheNewPerformance(int hospitalid, int year, int month)
        {
            string sql = @"select ifnull(sum(fee),0) as fee from (
select realgivefee as fee from res_compute t1 
	join per_allot t2 on t1.allotid=t2.id
where hospitalid=@hospitalid and year=@year and month=@month and accounttype in ('护士长','科主任','行政中层','行政高层')
union all
select AssessLaterPerforTotal as fee from res_account t1 
	join per_allot t2 on t1.allotid=t2.id
where hospitalid=@hospitalid and year=@year and month=@month
) tab";
            return perbudgetresultRepository.DapperQueryFirstOrDefault<decimal>(sql, new { hospitalid, year, month });
        }
        ///医疗收入
        private decimal GetMedicalIncome(int hospitalid, int year, int month)
        {
            string sql = @"select ifnull(sum(cellvalue),0) as fee from report_original_income t1 where hospitalid=@hospitalid and year=@year and month=@month";
            return perbudgetresultRepository.DapperQueryFirstOrDefault<decimal>(sql, new { hospitalid, year, month });
        }
        ///药品收入
        private decimal GetMedicineProportion(int hospitalid, int year, int month)
        {
            string sql = @"select ifnull(sum(cellvalue),0) as fee
from report_original_income t1
	join cof_drugtype t2 on t1.allotid = t2.allotid and t2.chargetype = '药费'
where t1.hospitalid=@hospitalid and t1.year=@year and t1.month=@month";
            return perbudgetresultRepository.DapperQueryFirstOrDefault<decimal>(sql, new { hospitalid, year, month });
        }
        ///材料支出
        private decimal GetMaterialCosts(int hospitalid, int year, int month)
        {
            string sql = @"select ifnull(sum(cellvalue),0) as fee
from report_original_income t1
	join cof_drugtype t2 on t1.allotid = t2.allotid and t2.chargetype = '材料费'
where t1.hospitalid=@hospitalid and t1.year=@year and t1.month=@month";
            return perbudgetresultRepository.DapperQueryFirstOrDefault<decimal>(sql, new { hospitalid, year, month });
        }
    }
}
