using Dapper;
using Microsoft.Extensions.Logging;
using Performance.DtoModels;
using Performance.EntityModels;
using Performance.Repository;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Performance.Services.ExtractExcelService
{
    public class DictionaryService : IAutoInjection
    {
        private readonly ILogger<DictionaryService> logger;
        private readonly QueryService queryService;
        private readonly LogManageService logService;
        private readonly PerforCofhrpdepartmentRepository cofhrpdepartmentRepository;
        private readonly PerforPeremployeeRepository peremployeeRepository;
        private readonly PerforHospitalconfigRepository hospitalconfigRepository;
        private readonly PerforExtypeRepository extypeRepository;
        private readonly PerforExscriptRepository exscriptRepository;
        private readonly PerforExresultRepository exresultRepository;
        private readonly PerforHisdataRepository hisdataRepository;
        private readonly PerforHisscriptRepository hisscriptRepository;

        public DictionaryService(
            ILogger<DictionaryService> logger,
            QueryService queryService,
            LogManageService logService,
            PerforCofhrpdepartmentRepository cofhrpdepartmentRepository,
            PerforPeremployeeRepository peremployeeRepository,
            PerforHospitalconfigRepository hospitalconfigRepository,
            PerforExtypeRepository extypeRepository,
            PerforExscriptRepository exscriptRepository,
            PerforExresultRepository exresultRepository,
            PerforHisdataRepository hisdataRepository,
            PerforHisscriptRepository hisscriptRepository
            )
        {
            this.logger = logger;
            this.queryService = queryService;
            this.logService = logService;
            this.cofhrpdepartmentRepository = cofhrpdepartmentRepository;
            this.peremployeeRepository = peremployeeRepository;
            this.hospitalconfigRepository = hospitalconfigRepository;
            this.extypeRepository = extypeRepository;
            this.exscriptRepository = exscriptRepository;
            this.exresultRepository = exresultRepository;
            this.hisdataRepository = hisdataRepository;
            this.hisscriptRepository = hisscriptRepository;
        }

        public void Handler(int hospitalId, per_allot allot, string groupName, bool isSingle)
        {
            try
            {
                var configs = hospitalconfigRepository.GetEntities(t => t.HospitalId == hospitalId);
                if (configs == null || !configs.Any()) throw new Exception("医院未配置绩效抽取信息");

                var types = extypeRepository.GetEntities(t => t.HospitalId == hospitalId && new int[] { (int)SheetType.Employee, (int)SheetType.OnlyExtract }.Contains(t.Source));
                if (types != null && types.Any())
                {
                    var scripts = exscriptRepository.GetEntities(t => t.TypeId.HasValue && types.Select(s => s.Id).Contains(t.TypeId.Value) && t.IsEnable == 1);
                    if (scripts != null && scripts.Any())
                    {
                        if (types.Any(t => t.Source == (int)SheetType.Employee))    //人员字典抽取配置
                        {
                            logService.ReturnTheLog(allot.ID, groupName, 2, "提取数据", $"提取人员信息数据", isSingle: isSingle);
                            var typeIds = types.Where(t => t.Source == (int)SheetType.Employee).Select(t => t.Id);
                            var script = scripts.Where(t => t.TypeId.HasValue && typeIds.Contains(t.TypeId.Value));
                            if (script != null)
                                Employee(allot, configs, scripts, isSingle);
                        }
                        if (types.Any(t => t.Source == (int)SheetType.OnlyExtract))    //不写入Excel的提取数据
                        {
                            logService.ReturnTheLog(allot.ID, groupName, 2, "提取数据", $"提取通用工作量数据", isSingle: isSingle);
                            foreach (var type in types.Where(t => t.Source == (int)SheetType.OnlyExtract))
                            {
                                var thisTypeScripts = scripts.Where(t => t.TypeId == type.Id);
                                if (thisTypeScripts == null || !thisTypeScripts.Any()) continue;

                                foreach (var script in thisTypeScripts)
                                {
                                    logger.LogInformation($"提取sql：   {script.ExecScript}");
                                    ExResult(allot, configs.FirstOrDefault(t => t.Id == script.ConfigId), script.ExecScript, type.EName, isSingle);
                                }
                            }
                        }
                    }
                }

                logService.ReturnTheLog(allot.ID, groupName, 2, "提取数据", $"开始提取HIS数据", isSingle: isSingle);

                var hisScrips = hisscriptRepository.GetEntities(t => t.HospitalId == hospitalId);
                if (hisScrips == null || !hisScrips.Any()) return;

                var hisdata = hisdataRepository.GetEntities(t => t.HospitalId == allot.HospitalId && t.Year == allot.Year && t.Month == allot.Month);
                if (hisdata != null && hisdata.Any())
                {
                    hisdataRepository.RemoveRange(hisdata.ToArray());
                }

                foreach (var item in hisScrips)
                {
                    HisData(allot, configs.FirstOrDefault(t => t.Id == item.ConfigId), item, groupName, isSingle);
                }
                logService.ReturnTheLog(allot.ID, groupName, 2, "提取数据", $"提取HIS数据完成", isSingle: isSingle);

            }
            catch (Exception)
            {
                logger.LogError("获取数据时发生异常");
                throw;
            }
        }

        private void Employee(per_allot allot, List<sys_hospitalconfig> configs, IEnumerable<ex_script> scripts, bool isSingle)
        {
            try
            {
                foreach (var script in scripts)
                {
                    var config = configs.FirstOrDefault(w => w.Id == script.ConfigId);
                    var sql = script.ExecScript;
                    if (config == null || string.IsNullOrEmpty(sql)) return;

                    var limitData = queryService.QueryData<dynamic>(config, sql, allot, isSingle);
                    if (limitData == null || !limitData.Any()) return;

                    var columns = ((IDictionary<string, object>)limitData.ElementAt(0)).Select(t => t.Key.ToLower()).ToList();
                    if (columns == null || !columns.Any()) return;

                    var data = queryService.QueryData<per_employee>(config, sql, allot, isSingle).ToList();
                    data.ForEach(t =>
                    {
                        t.AllotId = allot.ID;
                        t.HospitalId = allot.HospitalId;
                        t.IsHrpEmployee = 1;
                    });

                    var hrpDepartments = cofhrpdepartmentRepository.GetEntities(t => t.AllotId == allot.ID);
                    if (hrpDepartments != null && hrpDepartments.Any())
                    {
                        data = data.GroupJoin(hrpDepartments, outer => new { department = outer.Department }, inner => new { department = inner.HRPDepartment }, (outer, inner) => new { outer, inner }).Select(t =>
                        {
                            t.outer.AccountingUnit = t.inner?.FirstOrDefault()?.AccountingUnit ?? t.outer.AccountingUnit;
                            return t.outer;
                        }).ToList();
                    }

                    var employees = peremployeeRepository.GetEntities(t => t.AllotId == allot.ID);
                    if (employees == null || !employees.Any())
                    {
                        peremployeeRepository.AddRange(data.ToArray());
                        return;
                    }

                    var needAddData = data.Where(w => !employees.Select(t => t.PersonnelNumber).Contains(w.PersonnelNumber));
                    if (needAddData != null && needAddData.Any()) peremployeeRepository.AddRange(needAddData.ToArray());

                    var needRemarkData = employees.Where(w => !data.Select(t => t.PersonnelNumber).Contains(w.PersonnelNumber))?.ToList();
                    if (needRemarkData != null && needRemarkData.Any())
                    {
                        needRemarkData.ForEach(t => t.Remark = "HRP中无此工号数据");
                        peremployeeRepository.UpdateRange(needRemarkData.ToArray());
                    }

                    var needUpdateData = employees.Where(w => data.Select(t => t.PersonnelNumber).Contains(w.PersonnelNumber));
                    if (needUpdateData != null && needUpdateData.Any())
                    {
                        JudgeDataEqual(columns, needUpdateData.ToList(), data);
                    }
                }
            }
            catch (Exception ex)
            {
                logger.LogError("获取人员名单时发生异常：" + ex.ToString());
            }
        }

        private void JudgeDataEqual(List<string> columns, List<per_employee> emps, List<per_employee> hrps)
        {
            Dictionary<string, Func<per_employee, object>> dict = new Dictionary<string, Func<per_employee, object>>
            {
                { nameof(per_employee.AccountingUnit), (t) => t.AccountingUnit },
                { nameof(per_employee.Department), (t) => t.Department },
                { nameof(per_employee.DoctorName), (t) => t.DoctorName },
                { nameof(per_employee.PersonnelNumber), (t) => t.DoctorName },
                { nameof(per_employee.JobNumber), (t) => t.JobNumber },
                { nameof(per_employee.JobCategory), (t) => t.JobCategory },
                { nameof(per_employee.Duty), (t) => t.Duty },
                { nameof(per_employee.JobTitle), (t) => t.JobTitle },
                { nameof(per_employee.UnitType), (t) => t.UnitType },
                { nameof(per_employee.Attendance), (t) => t.Attendance },
                { nameof(per_employee.AttendanceDay), (t) => t.AttendanceDay },
                { nameof(per_employee.PermanentStaff), (t) => t.PermanentStaff },
                { nameof(per_employee.EfficiencyNumber), (t) => t.EfficiencyNumber },
                { nameof(per_employee.BirthDate), (t) => t.BirthDate },
                { nameof(per_employee.ReservedRatio), (t) => t.ReservedRatio },
                { nameof(per_employee.BankCard), (t) => t.BankCard },
            };

            columns = columns.Intersect(dict.Keys.Select(t => t.ToLower())).ToList();
            if (columns == null || !columns.Any()) return;

            if (columns.Contains(nameof(per_employee.PersonnelNumber).ToLower())) columns.Remove(nameof(per_employee.PersonnelNumber).ToLower());
            if (!columns.Contains(nameof(per_employee.AccountingUnit).ToLower())) columns.Add(nameof(per_employee.AccountingUnit).ToLower());

            List<per_employee> updateData = new List<per_employee>();
            foreach (var emp in emps)
            {
                var hrp = hrps.FirstOrDefault(w => w.PersonnelNumber == emp.PersonnelNumber);
                if (hrp == null) continue;

                foreach (var item in dict)
                {
                    if (!columns.Contains(item.Key.ToLower())) continue;

                    var empVal = item.Value.Invoke(emp);
                    var hrpVal = item.Value.Invoke(hrp);
                    if ((empVal == null && hrpVal != null) || (empVal != null && !empVal.Equals(hrpVal)))
                    {
                        hrp.Id = emp.Id;
                        updateData.Add(hrp);
                        break;
                    }
                }
            }

            if (updateData != null && updateData.Any())
            {
                string updateSql = $"update per_employee set {string.Join(",", columns.Select(t => $"{t}=@{t}"))} where id = @id";
                logger.LogInformation("同步hrp人员字典Sql语句: " + updateSql);
                peremployeeRepository.Execute(updateSql, updateData);
            }
        }

        private void HisData(per_allot allot, sys_hospitalconfig config, his_script script, string groupName, bool isSingle)
        {
            try
            {
                logService.ReturnTheLog(allot.ID, groupName, 2, "提取数据", $"提取“{script.SourceType} - {script.Category}”数据", isSingle: isSingle);

                if (config == null || string.IsNullOrEmpty(script.ExecScript)) return;

                var data = queryService.QueryData<HisData>(config, script.ExecScript, allot, isSingle);
                if (data == null || !data.Any()) return;

                var insertData = data.Select(t => new his_data
                {
                    HospitalId = allot.HospitalId,
                    AllotId = allot.ID,
                    Year = allot.Year,
                    Month = allot.Month,
                    HisDepartment = GetSaveValue(t.HisDepartment, t.Department, t.AccountingUnit),
                    PersonnelName = t.DoctorName,
                    PersonnelNumber = GetSaveValue(t.PersonnelNumber, t.DoctorName),
                    SourceType = script.SourceType,
                    Category = script.Category,
                    Original = GetSaveValue(t.Category, t.ItemName, t.Original),
                    Value = t.Value,
                    CreateTime = DateTime.Now,
                });
                hisdataRepository.AddRange(insertData.ToArray());
                logService.ReturnTheLog(allot.ID, groupName, 2, "提取数据", $"提取“{script.SourceType} - {script.Category}”完成", isSingle: isSingle);
            }
            catch (Exception ex)
            {
                logger.LogError("获取his_data时发生异常：" + ex.ToString());
                logService.ReturnTheLog(allot.ID, allot.ID.ToString(), 2, "SQL错误", $"获取HIS数据“{script.SourceType} - {script.Category}”时发生异常", 3, isSingle);
            }
        }

        private void Department()
        {
        }

        private void ExResult(per_allot allot, sys_hospitalconfig config, string sql, string enmae, bool isSingle)
        {
            try
            {
                if (config == null || string.IsNullOrEmpty(sql)) return;

                var data = queryService.QueryData<ExtractDto>(config, sql, allot, isSingle);
                if (data == null || !data.Any()) return;

                var createTime = DateTime.Now;
                var result = data.Select(t => new ex_result
                {
                    Department = t.Department,
                    Category = enmae,
                    Fee = t.Value,
                    DoctorName = t.DoctorName,
                    PersonnelNumber = t.PersonnelNumber,
                    Source = "通用工作量",
                    DatabaseType = config.DataBaseType,
                    ConfigId = config.Id,
                    AllotId = allot.ID,
                    CreateTime = createTime,
                    IsDelete = 0,
                }).ToList();
                exresultRepository.AddRange(result.ToArray());
            }
            catch (Exception ex)
            {
                logger.LogError("获取不写入Excel数据时发生异常：" + ex.ToString());
            }
        }

        private string GetSaveValue(params string[] arr)
        {
            string value = string.Empty;
            if (arr == null || !arr.Any())
                return value;
            return arr.FirstOrDefault(w => !string.IsNullOrEmpty(w));
        }

        /// <summary>
        /// 校正HRP人员的科室信息
        /// </summary>
        /// <param name="allotId"></param>
        public void CorrectionHRPDepartment(int allotId)
        {
            try
            {
                var employees = peremployeeRepository.GetEntities(t => t.AllotId == allotId && (t.IsHrpEmployee ?? 0) == 1);
                if (employees == null || !employees.Any()) return;

                var hrpDepartments = cofhrpdepartmentRepository.GetEntities(t => t.AllotId == allotId);
                if (hrpDepartments != null && hrpDepartments.Any())
                {
                    employees = employees.GroupJoin(hrpDepartments, outer => new { department = outer.Department }, inner => new { department = inner.HRPDepartment }, (outer, inner) => new { outer, inner }).Select(t =>
                    {
                        t.outer.AccountingUnit = t.inner?.FirstOrDefault()?.AccountingUnit;
                        return t.outer;
                    }).ToList();

                    peremployeeRepository.UpdateRange(employees.ToArray());
                }
            }
            catch (Exception ex)
            {
                logger.LogError($"修改HRP人员科室时发生异常：{ex}");
            }
        }
    }
}
