using Infrastructure.Model;
using NLog;
using Quartz;
using Quartz.Impl;
using Quartz.Impl.Matchers;
using Quartz.Impl.Triggers;
using Quartz.Spi;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using ZR.Model.System;

namespace ZR.Tasks
{
    /// <summary>
    /// 计划任务中心
    /// </summary>
    //[AppService]
    public class TaskSchedulerServer : ITaskSchedulerServer
    {
        private Task<IScheduler> _scheduler;
        private readonly IJobFactory _jobFactory;
        /// <summary>
        /// 日志接口
        /// </summary>
        private static Logger logger = LogManager.GetCurrentClassLogger();

        public TaskSchedulerServer(IJobFactory jobFactory)
        {
            _scheduler = GetTaskSchedulerAsync();
            _jobFactory = jobFactory;
        }

        /// <summary>
        /// 开启计划任务
        /// 参考文章：https://www.quartz-scheduler.net/documentation/quartz-3.x/configuration/reference.html#datasources-ado-net-jobstores
        /// </summary>
        /// <returns></returns>
        private Task<IScheduler> GetTaskSchedulerAsync()
        {
            if (_scheduler != null)
            {
                return _scheduler;
            }

            NameValueCollection collection = new NameValueCollection
            {
                { "quartz.serializer.type","binary" },
                //quartz参数
                //{ "quartz.scheduler.instanceId", "AUTO" },
                //{ "quartz.serializer.type","json" },
                ////下面为指定quartz持久化数据库的配置
                //{ "quartz.jobStore.type","Quartz.Impl.AdoJobStore.JobStoreTX, Quartz" },
                //{ "quartz.jobStore.tablePrefix","QRTZ_"},
                //{ "quartz.jobStore.driverDelegateType", "Quartz.Impl.AdoJobStore.MySQLDelegate, Quartz"},
                //{ "quartz.jobStore.useProperties", "true"},
                //{ "quartz.jobStore.dataSource", "myDS" },
                //{ "quartz.dataSource.myDS.connectionString", @"server=xxx.xxx.xxx.xxx;port=3306;database=Admin;uid=zrry;pwd=********;Charset=utf8;"},
                //{ "quartz.dataSource.myDS.provider", "MySql" },
            };

            StdSchedulerFactory factory = new StdSchedulerFactory(collection);

            return _scheduler = factory.GetScheduler();
        }

        public async Task<ApiResult> StartTaskScheduleAsync()
        {
            try
            {
                _scheduler.Result.JobFactory = _jobFactory;
                if (_scheduler.Result.IsStarted)
                {
                    return ApiResult.Error(500, $"计划任务已经开启");
                }

                //等待任务运行完成
                await _scheduler.Result.Start();
                return ApiResult.Success("计划任务开启成功");
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// 停止计划任务
        /// </summary>
        /// <returns></returns>
        public async Task<ApiResult> StopTaskScheduleAsync()
        {
            try
            {
                if (_scheduler.Result.IsShutdown)
                {
                    return ApiResult.Error(500, $"计划任务已经停止");
                }

                await _scheduler.Result.Shutdown();
                return ApiResult.Success("计划任务已经停止");
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// 添加一个计划任务
        /// </summary>
        /// <param name="tasksQz"></param>
        /// <returns></returns>
        public async Task<ApiResult> AddTaskScheduleAsync(SysTasks tasksQz)
        {
            try
            {
                JobKey jobKey = new JobKey(tasksQz.ID, tasksQz.JobGroup);
                if (await _scheduler.Result.CheckExists(jobKey))
                {
                    return ApiResult.Error(500, $"该计划任务已经在执行:【{tasksQz.Name}】,请勿重复添加！");
                }
                if (tasksQz?.EndTime <= DateTime.Now)
                {
                    return ApiResult.Error(500, $"结束时间小于当前时间计划将不会被执行");
                }
                #region 设置开始时间和结束时间

                tasksQz.BeginTime = tasksQz.BeginTime == null ? DateTime.Now : tasksQz.BeginTime;
                tasksQz.EndTime = tasksQz.EndTime == null ? DateTime.MaxValue.AddDays(-1) : tasksQz.EndTime;

                DateTimeOffset starRunTime = DateBuilder.NextGivenSecondDate(tasksQz.BeginTime, 1);//设置开始时间
                DateTimeOffset endRunTime = DateBuilder.NextGivenSecondDate(tasksQz.EndTime, 1);//设置暂停时间

                #endregion

                #region 通过反射获取程序集类型和类   

                Assembly assembly = Assembly.Load(new AssemblyName(tasksQz.AssemblyName));
                Type jobType = assembly.GetType(tasksQz.AssemblyName + "." + tasksQz.ClassName);

                #endregion
                //2、开启调度器。判断任务调度是否开启
                if (!_scheduler.Result.IsStarted)
                {
                    await StartTaskScheduleAsync();
                }

                //3、创建任务。传入反射出来的执行程序集
                IJobDetail job = new JobDetailImpl(tasksQz.ID, tasksQz.JobGroup, jobType);
                job.JobDataMap.Add("JobParam", tasksQz.JobParams);
                ITrigger trigger;

                //4、创建一个触发器
                if (tasksQz.Cron != null && CronExpression.IsValidExpression(tasksQz.Cron))
                {
                    trigger = CreateCronTrigger(tasksQz);
                    //解决Quartz启动后第一次会立即执行问题解决办法
                    ((CronTriggerImpl)trigger).MisfireInstruction = MisfireInstruction.CronTrigger.DoNothing;
                }
                else
                {
                    trigger = CreateSimpleTrigger(tasksQz);
                    ((SimpleTriggerImpl)trigger).MisfireInstruction = MisfireInstruction.CronTrigger.DoNothing;
                }

                // 5、将触发器和任务器绑定到调度器中
                await _scheduler.Result.ScheduleJob(job, trigger);
                //任务没有启动、暂停任务
                //if (!tasksQz.IsStart)
                //{
                //    _scheduler.Result.PauseJob(jobKey).Wait();
                //}
                //按新的trigger重新设置job执行
                await _scheduler.Result.ResumeTrigger(trigger.Key);
                return ApiResult.Success($"启动计划任务:【{tasksQz.Name}】成功！");
            }
            catch (Exception ex)
            {
                logger.Error(ex, $"启动计划任务失败，分组：{tasksQz.JobGroup},作业：【{tasksQz.Name}】,error：{ex.Message}");
                return ApiResult.Error(500, $"启动计划任务:【{tasksQz.Name}】失败！");
            }
        }

        /// <summary>
        /// 暂停指定的计划任务
        /// </summary>
        /// <param name="tasksQz"></param>
        /// <returns></returns>
        public async Task<ApiResult> PauseTaskScheduleAsync(SysTasks tasksQz)
        {
            try
            {
                JobKey jobKey = new JobKey(tasksQz.ID, tasksQz.JobGroup);
                if (await _scheduler.Result.CheckExists(jobKey))
                {
                    // 防止创建时存在数据问题 先移除，然后在执行创建操作
                    await _scheduler.Result.PauseJob(jobKey);
                }
                return ApiResult.Success($"暂停计划任务:【{tasksQz.Name}】成功");
            }
            catch (Exception ex)
            {
                logger.Error(ex);
                return new ApiResult(500, $"暂停计划任务:【{tasksQz.Name}】失败，{ex.Message}");
            }
        }

        /// <summary>
        /// 恢复指定计划任务
        /// </summary>
        /// <param name="tasksQz"></param>
        /// <returns></returns>
        public async Task<ApiResult> ResumeTaskScheduleAsync(SysTasks tasksQz)
        {
            try
            {
                JobKey jobKey = new JobKey(tasksQz.ID, tasksQz.JobGroup);
                if (!await _scheduler.Result.CheckExists(jobKey))
                {
                    return ApiResult.Error(500, $"未找到计划任务:【{tasksQz.Name}】");
                }
                await _scheduler.Result.ResumeJob(jobKey);
                return ApiResult.Success($"恢复计划任务:【{tasksQz.Name}】成功");
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// 删除指定计划任务
        /// </summary>
        /// <param name="tasksQz"></param>
        /// <returns></returns>
        public async Task<ApiResult> DeleteTaskScheduleAsync(SysTasks tasksQz)
        {
            try
            {
                JobKey jobKey = new JobKey(tasksQz.ID, tasksQz.JobGroup);

                //await _scheduler.Result.ScheduleJob()

                await _scheduler.Result.DeleteJob(jobKey);
                return ApiResult.Success($"删除计划任务:【{tasksQz.Name}】成功");
            }
            catch (Exception ex)
            {
                logger.Error(ex);
                return ApiResult.Error($"删除计划任务:【{tasksQz.Name}】失败, error={ex.Message}");
            }
        }

        /// <summary>
        /// 立即运行
        /// </summary>
        /// <param name="tasksQz"></param>
        /// <returns></returns>
        public async Task<ApiResult> RunTaskScheduleAsync(SysTasks tasksQz)
        {
            try
            {
                JobKey jobKey = new JobKey(tasksQz.ID, tasksQz.JobGroup);
                List<JobKey> jobKeys = _scheduler.Result.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(tasksQz.JobGroup)).Result.ToList();
                if (jobKeys == null || jobKeys.Count == 0)
                {
                    await AddTaskScheduleAsync(tasksQz);
                }

                var triggers = await _scheduler.Result.GetTriggersOfJob(jobKey);
                if (triggers.Count <= 0)
                {
                    return new ApiResult(110, $"未找到触发器[{jobKey.Name}]");
                }
                await _scheduler.Result.TriggerJob(jobKey);

                return ApiResult.Success($"运行计划任务:【{tasksQz.Name}】成功");
            }
            catch (Exception ex)
            {
                return new ApiResult(500, $"执行计划任务:【{tasksQz.Name}】失败，{ex.Message}");
            }
        }

        /// <summary>
        /// 更新计划任务
        /// </summary>
        /// <param name="tasksQz"></param>
        /// <returns></returns>
        public async Task<ApiResult> UpdateTaskScheduleAsync(SysTasks tasksQz)
        {
            try
            {
                JobKey jobKey = new JobKey(tasksQz.ID, tasksQz.JobGroup);
                if (await _scheduler.Result.CheckExists(jobKey))
                {
                    //防止创建时存在数据问题 先移除，然后在执行创建操作
                    await _scheduler.Result.DeleteJob(jobKey);
                }
                //await AddTaskScheduleAsync(tasksQz);
                return ApiResult.Success("修改计划成功");
            }
            catch (Exception ex)
            {
                logger.Error(ex);
                return ApiResult.Error($"修改计划失败，error={ex.Message}");
            }
        }

        #region 创建触发器帮助方法

        /// <summary>
        /// 创建SimpleTrigger触发器（简单触发器）
        /// </summary>
        /// <param name="tasksQz"></param>
        /// <returns></returns>
        private ITrigger CreateSimpleTrigger(SysTasks tasksQz)
        {
            if (tasksQz.RunTimes > 0)
            {
                ITrigger trigger = TriggerBuilder.Create()
                .WithIdentity(tasksQz.ID, tasksQz.JobGroup)
                .StartAt(tasksQz.BeginTime.Value)
                .EndAt(tasksQz.EndTime.Value)
                .WithSimpleSchedule(x => x.WithIntervalInSeconds(tasksQz.IntervalSecond)
                .WithRepeatCount(tasksQz.RunTimes)).ForJob(tasksQz.ID, tasksQz.JobGroup).Build();
                return trigger;
            }
            else
            {
                ITrigger trigger = TriggerBuilder.Create()
                .WithIdentity(tasksQz.ID, tasksQz.JobGroup)
                .StartAt(tasksQz.BeginTime.Value)
                .EndAt(tasksQz.EndTime.Value)
                .WithSimpleSchedule(x => x.WithIntervalInSeconds(tasksQz.IntervalSecond)
                .RepeatForever()).ForJob(tasksQz.ID, tasksQz.JobGroup).Build();
                return trigger;
            }
            // 触发作业立即运行，然后每10秒重复一次，无限循环
        }

        /// <summary>
        /// 创建类型Cron的触发器
        /// </summary>
        /// <param name="tasksQz"></param>
        /// <returns></returns>
        private ITrigger CreateCronTrigger(SysTasks tasksQz)
        {
            // 作业触发器
            return TriggerBuilder.Create()
                   .WithIdentity(tasksQz.ID, tasksQz.JobGroup)
                   .StartAt(tasksQz.BeginTime.Value)//开始时间
                   .EndAt(tasksQz.EndTime.Value)//结束数据
                   .WithCronSchedule(tasksQz.Cron)//指定cron表达式
                   .ForJob(tasksQz.ID, tasksQz.JobGroup)//作业名称
                   .Build();
        }

        #endregion

    }
}
