using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Performance.DtoModels;
using Performance.DtoModels.AppSettings;
using Performance.EntityModels;
using Performance.Infrastructure;
using Performance.Repository;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Text.RegularExpressions;

namespace Performance.Services
{
    public class ReportDataService : IAutoInjection
    {
        private readonly ILogger<ReportDataService> logger;
        private readonly PerforRepreportRepository reportRepository;
        private readonly PerforRepselectionRepository selectionRepository;
        private readonly PerforRepgroupRepository groupRepository;
        private readonly PerforRepgroupselectionRepository groupselectionRepository;
        private readonly PerforUserroleRepository userroleRepository;
        private readonly PerforRoleRepository roleRepository;
        private readonly PerforUserRepository userRepository;
        private readonly Application application;

        public ReportDataService(
            ILogger<ReportDataService> logger,
            PerforRepreportRepository reportRepository,
            PerforRepselectionRepository selectionRepository,
            PerforRepgroupRepository groupRepository,
            PerforRepgroupselectionRepository groupselectionRepository,
            PerforUserroleRepository userroleRepository,
            PerforRoleRepository roleRepository,
            PerforUserRepository userRepository,
            IOptions<Application> application)
        {
            this.logger = logger;
            this.reportRepository = reportRepository;
            this.selectionRepository = selectionRepository;
            this.groupRepository = groupRepository;
            this.groupselectionRepository = groupselectionRepository;
            this.userroleRepository = userroleRepository;
            this.roleRepository = roleRepository;
            this.userRepository = userRepository;
            this.application = application.Value;
        }

        /// <summary>
        /// 查询一组报表条件
        /// </summary>
        /// <param name="groupId"></param>
        /// <returns></returns>
        public List<SelectionOptions> GetReportSelection(int groupId, int userId)
        {
            List<SelectionOptions> options = new List<SelectionOptions>();

            var groups = groupselectionRepository.GetEntities(w => w.GroupId == groupId);

            var arr = groups.Select(w => w.SelectionId);

            var selections = selectionRepository.GetEntities(w => arr.Contains(w.ID))?.OrderBy(w => w.Sort);
            if (selections == null)
                return options;

            var isMedical = IsMedical(userId);

            var dispaly = new int[] { (int)SelectionState.UsableAndNotDispaly, (int)SelectionState.NotUsableAndNotDispaly };
            foreach (var item in selections.Where(t => t.State.HasValue && !dispaly.Contains(t.State.Value)))
            {
                SelectionOptionDefault handle = new SelectionOptionDefault();
                //if (item.LoadType == (int)LoadType.InstantLoad && item.Type == 2)
                //    handle = new SelectionOptionDynamic();
                SelectionOptions selection = new SelectionOptions(item);
                selection.Options = handle.GetOptions(item);
                if (isMedical)
                {
                    selection.Options.RemoveAll(t => t.Title == "全院");
                }
                options.Add(selection);
            }
            return options;
        }

        /// <summary>
        /// 获取一组报表信息
        /// </summary>
        /// <param name="groupId"></param>
        /// <returns></returns>
        public object GetReportInfo(int groupId)
        {
            var groups = groupRepository.GetEntities(w => w.GroupId == groupId);

            var arr = groups.Select(w => w.ReportId);

            var reports = reportRepository.GetEntities(w => arr.Contains(w.ID));

            return groups.Select(w => new
            {
                w.GroupId,
                w.ReportId,
                ReportName = reports.FirstOrDefault(r => r.ID == w.ReportId)?.Title
            });
        }

        /// <summary>
        /// 查询报表数据
        /// </summary>
        /// <param name="hospitalId"></param>
        /// <param name="groupId"></param>
        /// <param name="values"></param>
        /// <returns></returns>
        public List<ReportData> GetReportData(int hospitalId, int groupId, int reportId, List<SelectionValues> values, int userId)
        {
            var groups = groupRepository.GetEntities(w => w.GroupId == groupId);
            var arr1 = groups.Select(w => w.ReportId);

            var reports = reportRepository.GetEntities(w => arr1.Contains(w.ID));
            if (reports == null || !reports.Any())
                throw new PerformanceException("报表信息无效");

            if (reportId > 0)
                reports = reports.Where(w => w.ID == reportId).ToList();

            var formats = GetParameterFormats();
            var groupSelections = groupselectionRepository.GetEntities(w => w.GroupId == groupId);
            var arr2 = groupSelections?.Select(w => w.SelectionId) ?? new List<int?>();

            var dispaly = new int[] { (int)SelectionState.UsableAndDispaly, (int)SelectionState.UsableAndNotDispaly };
            var selections = selectionRepository.GetEntities(w => arr2.Contains(w.ID) && w.State.HasValue && dispaly.Contains(w.State.Value))
                ?? new List<rep_selection>();

            var isMedical = IsMedical(userId);
            var department = GetUserDepartment(userId);

            List<ReportData> result = new List<ReportData>();
            foreach (var report in reports)
            {
                var sql = report.Content.ToLower();

                //不重复条件，动态拼接WHERE条件
                string @where = "";
                if (selections != null && selections.Any())
                {
                    selections = GetNoRepeatSelections(sql, formats, selections);
                    @where = GetFilterSelection(selections, values);
                }
                @where += $" and hospitalid={hospitalId}";
                if (isMedical && sql.IndexOf("report_allot_crowd") < 0)
                    @where += $" and accountingunit='{department}'";
                // 固定占位符
                var pairs = PredefinePlaceholder(values);
                pairs.Add("wh", @where);
                pairs.Add("w", $"where 1=1 {@where}");
                pairs.Add("hospitalid", hospitalId);
                // 支持两种占位符
                pairs = CopyToFormats(pairs, formats);
                // 替换占位符
                sql = ReplacePlaceholder(selections, values, sql, formats);
                // 替换固定占位符数据
                foreach (var item in pairs)
                {
                    sql = Regex.Replace(sql, item.Key, item.Value.ToString(), RegexOptions.IgnoreCase);
                }
                // 执行SQL
                var chartData = reportRepository.DapperQuery<ChartData>(sql, null);

                ReportData reportData = new ReportData(report);
                reportData.Title = AddSelectionToReportTitle(selections, values, report.Title);
                reportData.ChartData = chartData != null && chartData.Any() ? chartData.ToList() : new List<ChartData>();
                result.Add(reportData);
            }
            return result;
        }

        /// <summary>
        /// 拼接报表WHERE条件
        /// </summary>
        /// <param name="selections"></param>
        /// <param name="values"></param>
        /// <returns></returns>
        private string GetFilterSelection(List<rep_selection> selections, List<SelectionValues> values)
        {
            string where = "";
            if (values == null || !values.Any())
                return where;
            foreach (var selection in selections)
            {
                var value = values.FirstOrDefault(w => w.Title == selection.InputName)?.Values;
                if (value == null || !value.Any())
                    continue;

                if (selection.Joint.Equals(SQLOperator.Equal.ToString(), StringComparison.OrdinalIgnoreCase))
                    where += $" and {selection.InputName}='{value.First()}'";
                else if (selection.Joint.Equals(SQLOperator.Like.ToString(), StringComparison.OrdinalIgnoreCase))
                    where += $" and {selection.InputName} like '%{value.First()}%'";
                else if (selection.Joint.Equals(SQLOperator.NotLike.ToString(), StringComparison.OrdinalIgnoreCase))
                    where += $" and {selection.InputName} not like '%{value.First()}%'";
                else if (selection.Joint.Equals(SQLOperator.In.ToString(), StringComparison.OrdinalIgnoreCase))
                    where += $" and {selection.InputName} in ({string.Join(",", value.Select(t => $"'{t}'"))})";
                else if (selection.Joint.Equals(SQLOperator.NotIn.ToString(), StringComparison.OrdinalIgnoreCase))
                    where += $" and {selection.InputName} not in ({string.Join(",", value.Select(t => $"'{t}'"))})";
            }
            return where;
        }


        /// <summary>
        /// 返回SQL中占位参数格式
        /// </summary>
        /// <returns></returns>
        private string[] GetParameterFormats()
        {
            return new string[] { "{{{0}}}", "@{0}" };
        }

        /// <summary>
        /// 返回不会在SQL中已经占位的条件，防止条件中重复出现相同条件
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="formats"></param>
        /// <param name="selections"></param>
        /// <returns></returns>
        private List<rep_selection> GetNoRepeatSelections(string sql, string[] formats, List<rep_selection> selections)
        {
            Func<string[], string, bool> checkExist = (patterns, inpatName) =>
            {
                foreach (var pat in patterns)
                {
                    if (sql.IndexOf(string.Format(pat, inpatName), StringComparison.OrdinalIgnoreCase) > 0)
                        return true;
                }
                return false;
            };

            List<rep_selection> result = new List<rep_selection>();
            foreach (var item in selections)
            {
                if (!checkExist.Invoke(formats, item.InputName))
                {
                    result.Add(item);
                }
            }
            return result;
        }


        /// <summary>
        /// 附加条件内容到报表标题
        /// </summary>
        /// <param name="selections"></param>
        /// <param name="values"></param>
        /// <param name="title"></param>
        /// <returns></returns>
        private string AddSelectionToReportTitle(List<rep_selection> selections, List<SelectionValues> values, string title)
        {
            if (!selections.Any(t => t.AddTitle == 1))
                return title;

            string addition = "";
            foreach (var item in selections.Where(t => t.AddTitle == 1))
            {
                if (values.Any(w => w.Title.Equals(item.InputName, StringComparison.OrdinalIgnoreCase)))
                {
                    var array = values.Any(w => w.Title.Equals(item.InputName, StringComparison.OrdinalIgnoreCase));
                    addition += string.Join(",", array);
                }
            }
            return $"{addition}{title}";
        }

        /// <summary>
        /// 根据已经定义的SQL参数格式，生成占位及条件值
        /// </summary>
        /// <param name="pairs"></param>
        /// <param name="formats"></param>
        /// <returns></returns>
        private Dictionary<string, object> CopyToFormats(Dictionary<string, object> pairs, string[] formats)
        {
            Dictionary<string, object> result = new Dictionary<string, object>();

            foreach (var item in pairs)
            {
                foreach (var patt in formats)
                {
                    result.Add(string.Format(patt, item.Key), item.Value);
                }
            }
            return result;
        }

        /// <summary>
        /// 预先定义SQL中固定占位符
        /// </summary>
        /// <param name="values"></param>
        /// <returns></returns>
        private Dictionary<string, object> PredefinePlaceholder(List<SelectionValues> values)
        {
            Dictionary<string, object> pairs = new Dictionary<string, object>();
            string[] keys1 = new string[] { "year", "month", };
            foreach (var key in keys1)
            {
                var list = values.FirstOrDefault(w => w.Title.ToLower() == key)?.Values;
                if (list != null && list.Any())
                {
                    var arr = Array.ConvertAll(list.ToArray(), input => int.Parse(input));
                    pairs.Add(key, arr.Max());
                }
            }

            if (pairs.Keys.Contains("year"))
            {
                var val = pairs.GetValue<int>("year");
                pairs.Add("yesteryear", val - 1);
            }
            if (pairs.Keys.Contains("month"))
            {
                var val = pairs.GetValue<int>("month");
                pairs.Add("lastmonth", val - 1);
            }

            return pairs;
        }

        /// <summary>
        /// 替换报表占位
        /// </summary>
        /// <param name="selections"></param>
        /// <param name="values"></param>
        /// <param name="content"></param>
        /// <param name="formats"></param>
        /// <returns></returns>
        private string ReplacePlaceholder(List<rep_selection> selections, List<SelectionValues> values, string content, string[] formats)
        {
            foreach (var selection in selections)
            {
                var value = values.FirstOrDefault(w => w.Title == selection.InputName)?.Values;
                if (value == null || !value.Any())
                    continue;
                foreach (var pattern in formats)
                {
                    if (selection.Joint.Equals(SQLOperator.Equal.ToString(), StringComparison.OrdinalIgnoreCase))
                        content = Regex.Replace(content, string.Format(pattern, selection.InputName), $"'{value.First()}'", RegexOptions.IgnoreCase);
                    else if (selection.Joint.Equals(SQLOperator.Like.ToString(), StringComparison.OrdinalIgnoreCase)
                        || selection.Joint.Equals(SQLOperator.NotLike.ToString(), StringComparison.OrdinalIgnoreCase))
                        content = Regex.Replace(content, string.Format(pattern, selection.InputName), $"'%{value.First()}%'", RegexOptions.IgnoreCase);
                    else if (selection.Joint.Equals(SQLOperator.In.ToString(), StringComparison.OrdinalIgnoreCase)
                        || selection.Joint.Equals(SQLOperator.NotIn.ToString(), StringComparison.OrdinalIgnoreCase))
                        content = Regex.Replace(content, string.Format(pattern, selection.InputName), $"{string.Join(",", value.Select(t => $"'{t}'"))}", RegexOptions.IgnoreCase);
                }
            }
            return content;
        }

        private bool IsMedical(int userId)
        {
            var userrole = userroleRepository.GetEntity(t => t.UserID == userId);
            var role = roleRepository.GetEntity(t => t.ID == userrole.RoleID);
            var roleTypes = new[] { application.NurseRole, application.DirectorRole, application.SpecialRole, application.OfficeRole };
            if (role.Type.HasValue && roleTypes.Contains(role.Type.Value))
                return true;
            else
                return false;
        }

        private string GetUserDepartment(int userId)
        {
            var user = userRepository.GetEntity(t => t.ID == userId);
            return user.Department;
        }
    }


    public class SelectionOptionDefault
    {
        public virtual List<TitleValue> GetOptions(rep_selection selection)
        {
            List<TitleValue> values = new List<TitleValue>();
            if (selection.Type != 1)
                return values;

            if (string.IsNullOrEmpty(selection.Content))
                return values;

            var selectItem = selection.Content.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
            values = selectItem.Select(t =>
            {
                string[] arr = t.Split('|');
                var title = arr[0];
                var value = arr.Length > 1 ? arr[1] : arr[0];

                var state = value == selection.DefaultValue ? 1 : 2;
                return new TitleValue(title, value, state);
            }).ToList();

            return values;
        }
    }
    public class SelectionOptionDynamic : SelectionOptionDefault
    {
        public override List<TitleValue> GetOptions(rep_selection selection)
        {
            //List<TitleValue> values = new List<TitleValue>();
            //if (selection.Type != 1)
            //    return values;

            //if (string.IsNullOrEmpty(selection.Content))
            //    return values;

            return base.GetOptions(selection);
        }
    }
}
