//------------------------------------------------------------------------------
//  此代码版权声明为全文件覆盖，如有原作者特别声明，会在下方手动补充
//  此代码版权（除特别声明外的代码）归作者本人Diego所有
//  源代码使用协议遵循本仓库的开源协议及附加协议
//  Gitee源代码仓库：https://gitee.com/diego2098/ThingsGateway
//  Github源代码仓库：https://github.com/kimdiego2098/ThingsGateway
//  使用文档：https://thingsgateway.cn/
//  QQ群：605534569
//------------------------------------------------------------------------------

using Microsoft.Extensions.Logging;

using ThingsGateway.Common.Extension;
using ThingsGateway.Gateway.Application.Extensions;
using ThingsGateway.NewLife;
using ThingsGateway.NewLife.Extension;

namespace ThingsGateway.Gateway.Application;

/// <summary>
/// 设备采集报警后台服务
/// </summary>
internal sealed class AlarmTask : IDisposable
{
    private readonly ILogger _logger;
    private ScheduledSyncTask scheduledTask;
    public AlarmTask(ILogger logger)
    {
        _logger = logger;
    }

    public void StartTask(CancellationToken cancellationToken)
    {
        _logger.LogInformation(AppResource.RealAlarmTaskStart);
        scheduledTask = new ScheduledSyncTask(10, DoWork, null, null, cancellationToken);
        scheduledTask.Start();
    }

    public void StopTask()
    {
        _logger.LogInformation(AppResource.RealAlarmTaskStop);
        scheduledTask?.Stop();
    }
    public void Dispose()
    {
        StopTask();
        scheduledTask?.TryDispose();
    }


    #region 核心实现

    /// <summary>
    /// 获取bool报警类型
    /// </summary>
    /// <param name="tag">要检查的变量</param>
    /// <param name="limit">报警限制值</param>
    /// <param name="expressions">报警约束表达式</param>
    /// <param name="text">报警文本</param>
    /// <returns>报警类型枚举</returns>
    private static AlarmTypeEnum? GetBoolAlarmCode(VariableRuntime tag, out string limit, out string expressions, out string text)
    {
        limit = string.Empty; // 初始化报警限制值为空字符串
        expressions = string.Empty; // 初始化报警约束表达式为空字符串
        text = string.Empty; // 初始化报警文本为空字符串

        if (tag?.Value == null) // 检查变量是否为null或其值为null
        {
            return null; // 如果是，则返回null
        }

        if (tag.BoolCloseAlarmEnable && !tag.Value.ToBoolean(true)) // 检查是否启用了关闭报警功能，并且变量的布尔值为false
        {
            limit = false.ToString(); // 将报警限制值设置为"false"
            expressions = tag.BoolCloseRestrainExpressions!; // 获取关闭报警的约束表达式
            text = tag.BoolCloseAlarmText!; // 获取关闭报警时的报警文本
            return AlarmTypeEnum.Close; // 返回关闭报警类型枚举
        }

        if (tag.BoolOpenAlarmEnable && tag.Value.ToBoolean(false)) // 检查是否启用了开启报警功能，并且变量的布尔值为true
        {
            limit = true.ToString(); // 将报警限制值设置为"true"
            expressions = tag.BoolOpenRestrainExpressions!; // 获取开启报警的约束表达式
            text = tag.BoolOpenAlarmText!; // 获取开启报警时的报警文本
            return AlarmTypeEnum.Open; // 返回开启报警类型枚举
        }

        return null; // 如果不符合任何报警条件，则返回null
    }

    /// <summary>
    /// 获取自定义报警类型
    /// </summary>
    /// <param name="tag">要检查的变量</param>
    /// <param name="limit">报警限制值</param>
    /// <param name="expressions">报警约束表达式</param>
    /// <param name="text">报警文本</param>
    /// <returns>报警类型枚举</returns>
    private static AlarmTypeEnum? GetCustomAlarmDegree(VariableRuntime tag, out string limit, out string expressions, out string text)
    {
        limit = string.Empty; // 初始化报警限制值为空字符串
        expressions = string.Empty; // 初始化报警约束表达式为空字符串
        text = string.Empty; // 初始化报警文本为空字符串

        if (tag?.Value == null) // 检查变量是否为null或其值为null
        {
            return null; // 如果是，则返回null
        }

        if (tag.CustomAlarmEnable) // 检查是否启用了自定义报警功能
        {
            // 调用变量的CustomAlarmCode属性的GetExpressionsResult方法，传入变量的值，获取报警表达式的计算结果
            var result = tag.CustomAlarmCode.GetExpressionsResult(tag.Value, tag.DeviceRuntime?.Driver?.LogMessage);

            if (result is bool boolResult) // 检查计算结果是否为布尔类型
            {
                if (boolResult) // 如果计算结果为true
                {
                    limit = tag.CustomAlarmCode; // 将报警限制值设置为自定义报警代码
                    expressions = tag.CustomRestrainExpressions!; // 获取自定义报警时的报警约束表达式
                    text = tag.CustomAlarmText!; // 获取自定义报警时的报警文本
                    return AlarmTypeEnum.Custom; // 返回自定义报警类型枚举
                }
            }
        }

        return null; // 如果不符合自定义报警条件，则返回null
    }

    /// <summary>
    /// 获取decimal类型的报警类型
    /// </summary>
    /// <param name="tag">要检查的变量</param>
    /// <param name="limit">报警限制值</param>
    /// <param name="expressions">报警约束表达式</param>
    /// <param name="text">报警文本</param>
    /// <returns>报警类型枚举</returns>
    private static AlarmTypeEnum? GetDecimalAlarmDegree(VariableRuntime tag, out string limit, out string expressions, out string text)
    {
        limit = string.Empty; // 初始化报警限制值为空字符串
        expressions = string.Empty; // 初始化报警约束表达式为空字符串
        text = string.Empty; // 初始化报警文本为空字符串

        if (tag?.Value == null) // 检查变量是否为null或其值为null
        {
            return null; // 如果是，则返回null
        }

        // 检查是否启用了高高报警功能，并且变量的值大于高高报警的限制值
        if (tag.HHAlarmEnable && tag.Value.ToDecimal() > tag.HHAlarmCode.ToDecimal())
        {
            limit = tag.HHAlarmCode.ToString()!; // 将报警限制值设置为高高报警的限制值
            expressions = tag.HHRestrainExpressions!; // 获取高高报警的约束表达式
            text = tag.HHAlarmText!; // 获取高高报警时的报警文本
            return AlarmTypeEnum.HH; // 返回高高报警类型枚举
        }

        // 检查是否启用了高报警功能，并且变量的值大于高报警的限制值
        if (tag.HAlarmEnable && tag.Value.ToDecimal() > tag.HAlarmCode.ToDecimal())
        {
            limit = tag.HAlarmCode.ToString()!; // 将报警限制值设置为高报警的限制值
            expressions = tag.HRestrainExpressions!; // 获取高报警的约束表达式
            text = tag.HAlarmText!; // 获取高报警时的报警文本
            return AlarmTypeEnum.H; // 返回高报警类型枚举
        }


        // 检查是否启用了低低报警功能，并且变量的值小于低低报警的限制值
        if (tag.LLAlarmEnable && tag.Value.ToDecimal() < tag.LLAlarmCode.ToDecimal())
        {
            limit = tag.LLAlarmCode.ToString()!; // 将报警限制值设置为低低报警的限制值
            expressions = tag.LLRestrainExpressions!; // 获取低低报警的约束表达式
            text = tag.LLAlarmText!; // 获取低低报警时的报警文本
            return AlarmTypeEnum.LL; // 返回低低报警类型枚举
        }


        // 检查是否启用了低报警功能，并且变量的值小于低报警的限制值
        if (tag.LAlarmEnable && tag.Value.ToDecimal() < tag.LAlarmCode.ToDecimal())
        {
            limit = tag.LAlarmCode.ToString()!; // 将报警限制值设置为低报警的限制值
            expressions = tag.LRestrainExpressions!; // 获取低报警的约束表达式
            text = tag.LAlarmText!; // 获取低报警时的报警文本
            return AlarmTypeEnum.L; // 返回低报警类型枚举
        }

        return null; // 如果不符合任何报警条件，则返回null
    }

    /// <summary>
    /// 对变量进行报警分析，并根据需要触发相应的报警事件或恢复事件。
    /// </summary>
    /// <param name="item">要进行报警分析的变量</param>
    private static void AlarmAnalysis(VariableRuntime item)
    {
        string limit; // 报警限制值
        string ex; // 报警约束表达式
        string text; // 报警文本
        AlarmTypeEnum? alarmEnum; // 报警类型枚举
        int delay = item.AlarmDelay; // 获取报警延迟时间

        // 检查变量的数据类型
        if (item.Value?.GetType() == typeof(bool))
        {
            // 如果数据类型为布尔型，则调用GetBoolAlarmCode方法获取布尔型报警类型及相关信息
            alarmEnum = GetBoolAlarmCode(item, out limit, out ex, out text);
        }
        else
        {
            // 如果数据类型为非布尔型，则调用GetDecimalAlarmDegree方法获取数值型报警类型及相关信息
            alarmEnum = GetDecimalAlarmDegree(item, out limit, out ex, out text);
        }

        // 如果未获取到报警类型，则尝试获取自定义报警类型
        if (alarmEnum == null)
        {
            alarmEnum = GetCustomAlarmDegree(item, out limit, out ex, out text);
        }

        if (alarmEnum == null)
        {
            // 如果仍未获取到报警类型，则触发需恢复报警事件（如果存在）
            AlarmChange(item, null, text, EventTypeEnum.Finish, alarmEnum, delay);
        }
        else
        {
            // 如果获取到了报警类型，则需触发报警事件或更新报警状态

            if (!string.IsNullOrEmpty(ex))
            {
                // 如果存在报警约束表达式，则计算表达式结果，以确定是否触发报警事件
                var data = ex.GetExpressionsResult(item.Value, item.DeviceRuntime?.Driver?.LogMessage);
                if (data is bool result)
                {
                    if (result)
                    {
                        // 如果表达式结果为true，则触发报警事件
                        AlarmChange(item, limit, text, EventTypeEnum.Alarm, alarmEnum, delay);
                    }
                }
            }
            else
            {
                // 如果不存在报警约束表达式，则直接触发报警事件
                AlarmChange(item, limit, text, EventTypeEnum.Alarm, alarmEnum, delay);
            }
        }
    }

    /// <summary>
    /// 根据报警事件类型进行相应的处理操作，包括触发报警事件或更新报警状态。
    /// </summary>
    /// <param name="item">要处理的变量</param>
    /// <param name="limit">报警限制值</param>
    /// <param name="text">报警文本</param>
    /// <param name="eventEnum">报警事件类型枚举</param>
    /// <param name="alarmEnum">报警类型枚举</param>
    /// <param name="delay">报警延时</param>
    private static void AlarmChange(VariableRuntime item, object limit, string text, EventTypeEnum eventEnum, AlarmTypeEnum? alarmEnum, int delay)
    {
        bool changed = false;
        if (eventEnum == EventTypeEnum.Finish)
        {
            // 如果是需恢复报警事件
            // 如果实时报警列表中不存在该变量，则直接返回
            if (!GlobalData.RealAlarmIdVariables.ContainsKey(item.Id))
            {
                return;
            }
        }
        else if (eventEnum == EventTypeEnum.Alarm)
        {
            // 如果是触发报警事件
            // 在实时报警列表中查找该变量
            if (GlobalData.RealAlarmIdVariables.TryGetValue(item.Id, out var variable))
            {
                // 如果变量已经处于相同的报警类型，则直接返回
                if (item.AlarmType == alarmEnum)
                    return;
            }
        }

        // 更新变量的报警信息和事件时间
        if (eventEnum == EventTypeEnum.Alarm)
        {
            var now = DateTime.Now;
            //添加报警延时策略
            if (delay > 0)
            {
                if (item.EventType != EventTypeEnum.Alarm && item.EventType != EventTypeEnum.Prepare)
                {
                    item.EventType = EventTypeEnum.Prepare;//准备报警
                    item.PrepareEventTime = now;
                }
                else
                {
                    if (item.EventType == EventTypeEnum.Prepare)
                    {
                        if ((now - item.PrepareEventTime!.Value).TotalSeconds > delay)
                        {
                            //超过延时时间，触发报警
                            item.EventType = EventTypeEnum.Alarm;
                            item.AlarmTime = now;
                            item.EventTime = now;
                            item.AlarmType = alarmEnum;
                            item.AlarmLimit = limit.ToString();
                            item.AlarmCode = item.Value.ToString();
                            item.RecoveryCode = string.Empty;
                            item.AlarmText = text;
                            item.PrepareEventTime = null;

                            changed = true;
                        }
                    }
                    else if (item.EventType == EventTypeEnum.Alarm && item.AlarmType != alarmEnum)
                    {
                        //报警类型改变，重新计时
                        if (item.PrepareEventTime == null)
                            item.PrepareEventTime = now;
                        if ((now - item.PrepareEventTime!.Value).TotalSeconds > delay)
                        {
                            //超过延时时间，触发报警
                            item.EventType = EventTypeEnum.Alarm;
                            item.AlarmTime = now;
                            item.EventTime = now;
                            item.AlarmType = alarmEnum;
                            item.AlarmLimit = limit.ToString();
                            item.AlarmCode = item.Value.ToString();
                            item.RecoveryCode = string.Empty;
                            item.AlarmText = text;
                            item.PrepareEventTime = null;
                            changed = true;
                        }

                    }
                    else
                    {
                        return;
                    }
                }
            }
            else
            {
                // 如果是触发报警事件
                item.EventType = eventEnum;
                item.AlarmTime = now;
                item.EventTime = now;
                item.AlarmType = alarmEnum;
                item.AlarmLimit = limit.ToString();
                item.AlarmCode = item.Value.ToString();
                item.RecoveryCode = string.Empty;
                item.AlarmText = text;
                changed = true;
            }


        }
        else if (eventEnum == EventTypeEnum.Finish)
        {
            // 如果是需恢复报警事件
            // 获取旧的报警信息
            if (GlobalData.RealAlarmIdVariables.TryGetValue(item.Id, out var oldAlarm))
            {
                item.AlarmType = oldAlarm.AlarmType;
                item.EventType = eventEnum;
                item.AlarmLimit = oldAlarm.AlarmLimit;
                item.AlarmCode = oldAlarm.AlarmCode;
                item.RecoveryCode = item.Value.ToString();
                item.AlarmText = oldAlarm.AlarmText;
                item.EventTime = DateTime.Now;
            }
            changed = true;
        }

        // 触发报警变化事件
        if (changed)
        {
            if (item.EventType == EventTypeEnum.Alarm)
            {
                // 如果是触发报警事件
                //lock (GlobalData. RealAlarmVariables)
                {
                    // 从实时报警列表中移除旧的报警信息，并添加新的报警信息
                    GlobalData.RealAlarmIdVariables.AddOrUpdate(item.Id, a => item.AdaptAlarmVariable(), (a, b) => item.AdaptAlarmVariable());
                }
            }
            else if (item.EventType == EventTypeEnum.Finish)
            {
                // 如果是需恢复报警事件，则从实时报警列表中移除该变量
                GlobalData.RealAlarmIdVariables.TryRemove(item.Id, out _);
                //GlobalData.RealAlarmIdVariables.AddOrUpdate(item.Id, a => item.AdaptAlarmVariable(), (a, b) => item.AdaptAlarmVariable());
            }
            GlobalData.AlarmChange(item.AdaptAlarmVariable());
        }

    }

    public void ConfirmAlarm(long variableId)
    {
        // 如果是确认报警事件
        if (GlobalData.AlarmEnableIdVariables.TryGetValue(variableId, out var variableRuntime))
        {
            variableRuntime.EventType = EventTypeEnum.Confirm;
            variableRuntime.EventTime = DateTime.Now;
            GlobalData.RealAlarmIdVariables.AddOrUpdate(variableId, a => variableRuntime.AdaptAlarmVariable(), (a, b) => variableRuntime.AdaptAlarmVariable());
            GlobalData.AlarmChange(variableRuntime.AdaptAlarmVariable());
        }
    }

    #endregion 核心实现

    /// <summary>
    /// 执行工作任务，对设备变量进行报警分析。
    /// </summary>
    /// <param name="state"></param>
    /// <param name="cancellation">取消任务的 CancellationToken</param>
    private void DoWork(object? state, CancellationToken cancellation)
    {

        try
        {
            if (!GlobalData.StartBusinessChannelEnable)
                return;

            //Stopwatch stopwatch = Stopwatch.StartNew();
            // 遍历设备变量列表

            if (!GlobalData.AlarmEnableIdVariables.IsEmpty)
            {
                var list = GlobalData.AlarmEnableIdVariables.Select(a => a.Value).ToArray();
                list.ParallelForEach((item, state, index) =>
            {
                {
                    // 如果取消请求已经被触发，则结束任务
                    if (cancellation.IsCancellationRequested)
                        return;

                    // 如果该变量的报警功能未启用，则跳过该变量
                    if (!item.AlarmEnable)
                        return;

                    // 如果该变量离线，则跳过该变量
                    if (!item.IsOnline)
                        return;

                    // 对该变量进行报警分析
                    AlarmAnalysis(item);


                }
            });
            }
            else
            {
                //if (scheduledTask.Period != 5000)
                //    scheduledTask.Change(0, 5000); // 如果没有启用报警的变量，则设置下次执行时间为5秒后
                scheduledTask.SetNext(5000); // 如果没有启用报警的变量，则设置下次执行时间为5秒后
            }

            //stopwatch.Stop();
            //_logger.LogInformation("报警分析耗时：" + stopwatch.ElapsedMilliseconds + "ms");
        }
        catch (OperationCanceledException)
        {
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "Alarm analysis fail");
        }
    }





}
