using AutoMapper;
using Microsoft.Extensions.Logging;
using Performance.DtoModels;
using Performance.EntityModels;
using Performance.Infrastructure;
using Performance.Infrastructure.Models;
using Performance.Repository;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Performance.Services.ExtractExcelService
{
    public class ExtractPreConfigService : IAutoInjection
    {
        private readonly ILogger logger;
        private readonly IMapper mapper;
        private readonly PerforHospitalRepository hospitalRepository;
        private readonly PerforHospitalconfigRepository hospitalconfigRepository;
        private readonly PerforExtypeRepository extypeRepository;
        private readonly PerforExscriptRepository exscriptRepository;
        private readonly PerforExmoduleRepository exmoduleRepository;
        private readonly PerforExitemRepository exitemRepository;
        private readonly PerforExspecialRepository exspecialRepository;

        private readonly QueryService queryService;

        public ExtractPreConfigService(
            ILogger<ExtractPreConfigService> logger,
            IMapper mapper,
            PerforHospitalRepository hospitalRepository,
            PerforHospitalconfigRepository hospitalconfigRepository,
            PerforExtypeRepository extypeRepository,
            PerforExscriptRepository exscriptRepository,
            PerforExmoduleRepository exmoduleRepository,
            PerforExitemRepository exitemRepository,
            PerforExspecialRepository exspecialRepository,
            QueryService queryService)
        {
            this.logger = logger;
            this.mapper = mapper;
            this.hospitalRepository = hospitalRepository;
            this.hospitalconfigRepository = hospitalconfigRepository;
            this.extypeRepository = extypeRepository;
            this.exscriptRepository = exscriptRepository;
            this.exmoduleRepository = exmoduleRepository;
            this.exitemRepository = exitemRepository;
            this.exspecialRepository = exspecialRepository;
            this.queryService = queryService;
        }

        #region HospitalConfig

        public List<sys_hospitalconfig> GetHospitalConfig(int hospitalId)
        {
            var hospital = hospitalRepository.GetEntity(w => w.ID == hospitalId);
            if (hospital == null) throw new PerformanceException("医院信息错误");

            var data = hospitalconfigRepository.GetEntities(w => w.HospitalId == hospitalId);
            var dic = GetDatatypes();
            return data;
        }

        public bool CreateHospitalConfig(sys_hospitalconfig hospitalconfig)
        {
            if (hospitalconfig.HospitalId == 0) throw new PerformanceException("参数hospitalid为空");

            if (string.IsNullOrEmpty(hospitalconfig.DbSource) || string.IsNullOrEmpty(hospitalconfig.DbName)
                || string.IsNullOrEmpty(hospitalconfig.DbUser) || string.IsNullOrEmpty(hospitalconfig.DbPassword))
                throw new PerformanceException("配置信息不可为空");

            var databases = EnumHelper.GetItems<DatabaseType>();
            if (!databases.Select(t => t.Value).Contains(hospitalconfig.DataBaseType))
                throw new PerformanceException("数据库类型错误");

            var hospital = hospitalRepository.GetEntity(w => w.ID == hospitalconfig.HospitalId);
            if (hospital == null) throw new PerformanceException("医院信息错误");

            var config = hospitalconfigRepository.GetEntity(w => w.ConfigName == hospitalconfig.ConfigName && w.HospitalId == hospitalconfig.HospitalId);
            if (config != null) throw new PerformanceException("连接名称重复");

            var result = hospitalconfigRepository.Add(hospitalconfig);
            if (result)
                TestConnectionCleared(hospitalconfig.Id);

            return result;
        }

        public bool UpdateHospitalConfig(sys_hospitalconfig hospitalconfig)
        {
            if (string.IsNullOrEmpty(hospitalconfig.DbSource) || string.IsNullOrEmpty(hospitalconfig.DbName)
                || string.IsNullOrEmpty(hospitalconfig.DbUser) || string.IsNullOrEmpty(hospitalconfig.DbPassword))
                throw new PerformanceException("配置信息不可为空");

            var databases = EnumHelper.GetItems<DatabaseType>();
            if (!databases.Select(t => t.Value).Contains(hospitalconfig.DataBaseType))
                throw new PerformanceException("数据库类型错误");

            var config = hospitalconfigRepository.GetEntity(w => w.ConfigName == hospitalconfig.ConfigName && w.HospitalId == hospitalconfig.HospitalId && w.Id != hospitalconfig.Id);
            if (config != null) throw new PerformanceException("连接名称重复");

            var entity = hospitalconfigRepository.GetEntity(w => w.Id == hospitalconfig.Id);
            if (entity == null) throw new PerformanceException("医院配置信息为空");

            entity.DataBaseType = hospitalconfig.DataBaseType;
            entity.DbSource = hospitalconfig.DbSource;
            entity.DbName = hospitalconfig.DbName;
            entity.DbUser = hospitalconfig.DbUser;
            entity.DbPassword = hospitalconfig.DbPassword;

            return hospitalconfigRepository.Update(entity);
        }

        public bool DeleteHospitalConfig(int hospitalconfigId)
        {
            var entity = hospitalconfigRepository.GetEntity(w => w.Id == hospitalconfigId);
            if (entity == null) throw new PerformanceException("医院配置信息为空");

            return hospitalconfigRepository.Remove(entity);
        }

        public bool TestConnectionCleared(int hospitalconfigId)
        {
            var entity = hospitalconfigRepository.GetEntity(w => w.Id == hospitalconfigId);
            if (entity == null) throw new PerformanceException("医院配置信息为空");

            var result = false;
            try
            {
                var connstr = ConnectionBuilder.GetConnectionString((DatabaseType)entity.DataBaseType, entity.DbSource, entity.DbName, entity.DbUser, entity.DbPassword);
                if (string.IsNullOrEmpty(connstr)) return result;

                var conn = ConnectionBuilder.Create((DatabaseType)entity.DataBaseType, connstr);
                if (conn == null) return result;

                conn.Open();
                result = true;

                entity.IsConnectioned = true;
                hospitalconfigRepository.Update(entity);
            }
            catch (Exception ex)
            {
                logger.LogError(ex.Message);
            }
            return result;
        }

        #endregion

        #region Extract Scripts

        public List<ExtractConfigResponse> GetExtractTypeAndScript(int hospitalId)
        {
            var sheettypes = GetSheettypes();

            var hospital = hospitalRepository.GetEntity(w => w.ID == hospitalId);
            if (hospital == null) throw new PerformanceException("医院信息错误");

            var data = extypeRepository.GetEntities(w => w.HospitalId == hospitalId);
            if (data == null || !data.Any()) return new List<ExtractConfigResponse>();

            var result = sheettypes.Where(w => data.Select(t => t.Source).Contains(w.Value))?
                .Select(t => new ExtractConfigResponse
                {
                    Value = t.Value,
                    Title = t.Title,
                    Source = t.Value
                }).OrderBy(t => t.Source).ToList();
            if (result == null || !result.Any()) return new List<ExtractConfigResponse>();

            var list = mapper.Map<List<ExtractConfigResponse>>(data);
            var scripts = exscriptRepository.GetEntities(w => data.Select(x => x.Id).Contains(w.TypeId)) ?? new List<ex_script>();
            var configs = hospitalconfigRepository.GetEntities(w => w.HospitalId == hospitalId) ?? new List<sys_hospitalconfig>();

            foreach (var source in result)
            {
                var sourcelist = list.Where(w => w.Source == source.Source);
                if (sourcelist == null || !sourcelist.Any()) continue;

                foreach (var item in sourcelist)
                {
                    var itemScripts = scripts.Where(w => w.TypeId == item.TypeId);
                    if (itemScripts == null || !itemScripts.Any()) continue;

                    item.TimeConsuming = itemScripts.Sum(t => t.TimeConsuming);
                    item.Children = mapper.Map<List<ExtractConfigResponse>>(itemScripts);

                    int index = 1;
                    item.Children.ForEach(x =>
                    {
                        var config = configs.FirstOrDefault(w => w.Id == x.ConfigId);
                        if (config != null)
                            x.ConfigName = string.IsNullOrEmpty(config.ConfigName) ? config.DbSource : config.ConfigName;

                        if (string.IsNullOrEmpty(x.Name))
                        {
                            x.Title = item.EName + index;
                            x.Name = item.EName + index;
                            index++;
                        }
                    });
                }
                source.Children = sourcelist.OrderBy(t => t.Name).ToList();
            }
            return result;
        }

        public ExtractConfigResponse GetExtractTypeAndScriptById(int typeId, int scriptId)
        {
            var type = extypeRepository.GetEntity(w => w.Id == typeId);
            if (type == null) throw new PerformanceException("参数无效");

            var data = mapper.Map<ExtractConfigResponse>(type);

            var script = exscriptRepository.GetEntity(w => w.TypeId == typeId && w.Id == scriptId) ?? new ex_script();
            if (script != null)
            {
                data.ExScriptId = script.Id;
                data.Name = script.Name;
                data.ExecScript = script.ExecScript;
                data.ConfigId = script.ConfigId;
                data.ConfigName = hospitalconfigRepository.GetEntity(w => w.Id == script.ConfigId)?.ConfigName;
                data.TimeConsuming = script.TimeConsuming;
                data.IsExecSuccess = script.IsExecSuccess;
                data.IsEnable = script.IsEnable;
            }

            return data;
        }

        public bool DeleteExtractTypeAndScript(int typeId, int scriptId)
        {
            if (scriptId != 0)
            {
                var script = exscriptRepository.GetEntity(w => w.TypeId == typeId && w.Id == scriptId);
                if (script == null) throw new PerformanceException("获取提取语句错误");

                return exscriptRepository.Remove(script);
            }
            else
            {
                var extype = extypeRepository.GetEntity(w => w.Id == typeId);
                if (extype == null) throw new PerformanceException("参数typeId无效");

                var scripts = exscriptRepository.GetEntities(w => w.TypeId == typeId);
                if (scripts != null && scripts.Any() && !exscriptRepository.RemoveRange(scripts.ToArray()))
                    throw new PerformanceException("提取语句删除失败");

                return extypeRepository.Remove(extype);
            }
        }

        public bool EditExtractTypeAndScript(int hospitalId, ExtractConfigResponse request)
        {
            if (request == null) throw new ArgumentNullException(nameof(request));

            if (string.IsNullOrEmpty(request.EName)) throw new PerformanceException("费用名称不可为空");

            var sheettypes = GetSheettypes();
            if (!sheettypes.Any(w => w.Value == request.Source)) throw new PerformanceException("暂不支持该类型");

            var type = extypeRepository.GetEntity(w => w.EName == request.EName && w.HospitalId == hospitalId && w.Id != request.TypeId);
            if (type != null) throw new PerformanceException("费用名称重复");

            if (request.TypeId == 0)
            {
                var entity = mapper.Map<ex_type>(request);
                entity.HospitalId = hospitalId;
                if (!extypeRepository.Add(entity)) return false;

                if (!string.IsNullOrEmpty(request.ExecScript))
                {
                    var script = new ex_script
                    {
                        Name = request.Name,
                        ExecScript = request.ExecScript,
                        ConfigId = request.ConfigId,
                        IsEnable = request.IsEnable,
                        TypeId = entity.Id
                    };
                    exscriptRepository.Add(script);
                }
            }
            else
            {
                var entity = extypeRepository.GetEntity(w => w.Id == request.TypeId);
                entity.EName = request.EName;
                entity.Description = request.Description;
                entity.Source = request.Source;
                if (!extypeRepository.Update(entity)) return false;

                if (request.ExScriptId != 0)
                {
                    var script = exscriptRepository.GetEntity(w => w.Id == request.ExScriptId);
                    if (script != null)
                    {
                        script.Name = request.Name;
                        script.ExecScript = request.ExecScript;
                        script.ConfigId = request.ConfigId;
                        script.IsEnable = request.IsEnable;
                        exscriptRepository.Update(script);
                    }
                }
                else
                {
                    var script = new ex_script
                    {
                        Name = request.Name,
                        ExecScript = request.ExecScript,
                        ConfigId = request.ConfigId,
                        IsEnable = request.IsEnable,
                        TypeId = entity.Id
                    };
                    exscriptRepository.Add(script);
                }
            }

            return true;
        }

        public dynamic ExecsqlAndGetResult(ConsumeTimeRequest request)
        {
            var config = hospitalconfigRepository.GetEntity(w => w.Id == request.ConfigId);
            if (config == null) throw new PerformanceException("数据库配置资源无效");

            var script = exscriptRepository.GetEntity(w => w.Id == request.ExScriptId);
            if (request.ExScriptId != 0 && script == null) throw new PerformanceException("参数无效");

            IEnumerable<dynamic> data = null;

            try
            {
                var begindate = request.Month > 0 && request.Year > 0 ? new DateTime(request.Year, request.Month, 1) : DateTime.Now.AddMonths(-1);
                var enddate = begindate.AddMonths(1);
                var param = new
                {
                    beginTime = $"{begindate.Year}-{begindate.Month.ToString().PadLeft(2, '0')}",
                    endTime = $"{enddate.Year}-{enddate.Month.ToString().PadLeft(2, '0')}"
                };

                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();
                data = queryService.QueryData<dynamic>(config, request.ExecScript, param);
                stopwatch.Stop();

                var timing = Math.Ceiling(stopwatch.ElapsedMilliseconds / 1000m);
                if (script != null)
                {
                    script.TimeConsuming = timing;
                    script.IsExecSuccess = 1;
                }
            }
            catch (Exception ex)
            {
                if (script != null)
                {
                    script.IsExecSuccess = 2;
                    script.Description = ex.Message;
                }

                throw new PerformanceException(ex.Message);
            }
            finally
            {
                if (script != null) exscriptRepository.Update(script);
            }

            if (data == null || !data.Any()) throw new PerformanceException("查询结果为空");

            var header = ((IDictionary<string, object>)data.ElementAt(0)).Keys;
            return new { header, data };
        }

        #endregion 

        #region 字典

        public static List<TitleValue<int>> GetDatatypes()
        {
            return EnumHelper.GetItems<DatabaseType>().Select(t => new TitleValue<int>
            {
                Title = t.Name,
                Value = t.Value
            }).ToList();
        }

        public static List<TitleValue<int>> GetSheettypes()
        {
            var showItems = new int[]
            {
                (int)SheetType.Employee,
                (int)SheetType.Income,
                (int)SheetType.OtherIncome,
                (int)SheetType.Expend,
                (int)SheetType.Workload,
                (int)SheetType.SpecialUnit,
                (int)SheetType.OtherWorkload,
                (int)SheetType.Assess,
                (int)SheetType.DoctorIncome,
                (int)SheetType.Custom,
                (int)SheetType.OnlyExtract
            };
            var list = EnumHelper.GetItems<SheetType>().Where(w => showItems.Contains(w.Value));

            var data =  list.Select(t => new TitleValue<int>
            {
                Title =  t.Description,
                Value = t.Value
            }).ToList();
            data.First(w => w.Value == (int)SheetType.Employee).Title = "HRP人员";
            return data;
        }

        public List<TitleValue<int>> GetExTypes(int hospitalId)
        {
            var hospital = hospitalRepository.GetEntity(w => w.ID == hospitalId);
            if (hospital == null) throw new PerformanceException("医院信息错误");

            var data = extypeRepository.GetEntities(w => w.HospitalId == hospitalId);
            if (data == null || !data.Any())
                return new List<TitleValue<int>>();

            return data.Select(t => new TitleValue<int>
            {
                Title = t.EName,
                Value = t.Id
            }).ToList();
        }

        public List<TitleValue<int>> GetConfigs(int hospitalId)
        {
            var hospital = hospitalRepository.GetEntity(w => w.ID == hospitalId);
            if (hospital == null) throw new PerformanceException("医院信息错误");

            var data = hospitalconfigRepository.GetEntities(w => w.HospitalId == hospitalId);
            if (data == null || !data.Any())
                return new List<TitleValue<int>>();

            return data.Select(t => new TitleValue<int>
            {
                Title = t.ConfigName,
                Value = t.Id
            }).ToList();
        }

        #endregion
    }
}
