using IACommService4CSharp;
using Model;
using PlcCom;
using PlcComponent;
using StandardLibrary;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace YangjieTester
{
    public class BackgroundService
    {
        public static BackgroundService Instance { get;  set; }

        public PlcDataMonitor MachineStateMonitor { get; private set; }
        public PlcDataMonitor AlarmMonitor { get; private set; }
        public PlcDataMonitor TipsMonitor { get; private set; }



        public IPlcComProtocol Protocol;

        private bool IsServiceAlive;
        private BackgroundService()
        {
        }
        public BackgroundService(IPlcComProtocol plcComProtocol):this()
        {
            this.Protocol = plcComProtocol;
            MachineStateMonitorInit();
            AlarmMonitorInit();
            TipsMonitorInit();
        }

        public bool Start()
        {
            if (IsServiceAlive)
                return false;

            StartAlarmTipsMonitor(0,200);
            IsServiceAlive = true;
            return true;
        }
        public void Stop()
        {
            if (IsServiceAlive)
            {
                StopAlarmTipsMonitor();
            }
        }

        void MachineStateMonitorInit()
        {
            MachineStateMonitor = new PlcDataMonitor();
            MachineStateMonitor.Protocol = Protocol;
            MachineStateMonitor.ReadNodes = AppSession.MachineState.PlcAddressPropertyPathsPairs
                                .Select(n => new Node(n.Key))
                                .ToList();

            MachineStateMonitor.OnMonitoredValuesChanged += MachineStateMonitor_OnMonitoredValuesChanged;
        }

        private void MachineStateMonitor_OnMonitoredValuesChanged(List<(Node node, object newValue)> obj)
        {
            foreach (var item in obj)
            {
                if (AppSession.MachineState.PlcAddressPropertyPathsPairs.TryGetValue(item.node.Value, out List<string> propertyPaths))
                {
                    foreach (var propertyPath in propertyPaths)
                    {
                        ObjectPropertySetter.SetPropertyValue(AppSession.MachineState, propertyPath, item.newValue);
                    }
                }
            }
        }

        void AlarmMonitorInit()
        {
            AlarmMonitor = new PlcDataMonitor();
            AlarmMonitor.Protocol=Protocol;
            AlarmMonitor.MonitorRules = Alarm.AlarmNodeWithMessage.Select(
            a => new MonitorRule()
            {
                Node = new Node(a.AlarmNode),
                Message = a.AlarmMessage,
                TargetValue = a.DesiredValue,
                CompareType= CompareType.ToTarget
                //MonitorType = MonitorType.RisingOrFallingEdge
            }).
            ToList();

            AlarmMonitor.OnDataTriggered += AlarmMonitor_OnDataTriggered;
            AlarmMonitor.OnDataCleared += AlarmMonitor_OnDataCleared;
        }
        private void AlarmMonitor_OnDataTriggered(object arg1, List<PlcDataMonitor.MonitorEventArgs> arg2)
        {
            AlarmService.LogBatch(arg2.Select(a => new Alarm() { AlarmMessage = a.Rule.Message, AlarmType = AlarmType.Error, Timestamp = a.Timestamp }), AppSession.CurrentUser);

            //AlarmService.Log(new Alarm() { AlarmMessage = arg2.Rule.Message, AlarmType = AlarmType.Error, Timestamp = arg2.Timestamp }, AppSession.CurrentUser);
        }
        private void AlarmMonitor_OnDataCleared(object arg1, List<PlcDataMonitor.MonitorEventArgs> arg2)
        {
            AlarmService.LogBatch(arg2.Select(a => new Alarm() { AlarmMessage = a.Rule.Message, AlarmType = AlarmType.Cleared, Timestamp = a.Timestamp }), AppSession.CurrentUser);
        }

        void TipsMonitorInit()
        {
            TipsMonitor = new PlcDataMonitor();
            TipsMonitor.Protocol = Protocol;
            TipsMonitor.MonitorRules = Tips.TipsNodeWithMessage.Select(
            t => new MonitorRule()
            {
                Node = new Node(t.Node),
                Message = t.Message,
                TargetValue = t.DesiredValue,
                MonitorType = MonitorType.RisingOrFallingEdge
            }).
            ToList();

            TipsMonitor.OnDataTriggered += TipsMonitor_OnDataTriggered;
            TipsMonitor.OnDataCleared += TipsMonitor_OnDataCleared;        
        }

        private void TipsMonitor_OnDataTriggered(object arg1, List<PlcDataMonitor.MonitorEventArgs> arg2)
        {
            TipsService.LogBatch(arg2.Select(a => new Tips() { Message = a.Rule.Message, Type = TipsType.Common, Timestamp = a.Timestamp }), AppSession.CurrentUser);
            ////TipsService.Log(new Tips() { Message = arg2.Rule.Message, Type = TipsType.Common , Timestamp = arg2.Timestamp }, AppSession.CurrentUser);
        }
        private void TipsMonitor_OnDataCleared(object arg1, List<PlcDataMonitor.MonitorEventArgs> arg2)
        {
            TipsService.LogBatch(arg2.Select(a => new Tips() { Message = a.Rule.Message, Type = TipsType.Cleared, Timestamp = a.Timestamp }), AppSession.CurrentUser);
            ////TipsService.Log(new Tips() { Message = arg2.Rule.Message, Type = TipsType.Cleared, Timestamp = arg2.Timestamp }, AppSession.CurrentUser);
        }

        Timer _timerMonitor;
        private void StartAlarmTipsMonitor(int dueTime,int interval)
        {
            if (_timerMonitor != null)
                return; // 避免重复创建

            var invalidNodes= AlarmMonitor.RemoveInvalidNodes();//剔除掉地址错误的节点
            foreach (var item in invalidNodes)
            {
                string err = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + $" {this.GetType().Name}->AlarmMonitor->{item}，读取失败";
                AppSession.ReadWriteErrors.Add(err);
            }

            invalidNodes = TipsMonitor.RemoveInvalidNodes();//剔除掉地址错误的节点
            foreach (var item in invalidNodes)
            {
                string err = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + $" {this.GetType().Name}->TipsMonitor->{item}，读取失败";
                AppSession.ReadWriteErrors.Add(err);
            }

            invalidNodes = MachineStateMonitor.RemoveInvalidNodes();//剔除掉地址错误的节点
            foreach (var item in invalidNodes)
            {
                string err = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + $" {this.GetType().Name}->TipsMonitor->{item}，读取失败";
                AppSession.ReadWriteErrors.Add(err);
            }


            // 创建 Timer
            // dueTime: intervalms 后第一次执行
            // period: 之后每 intervalms 执行一次
            _timerMonitor = new Timer(
                callback: OnTimerElapsed,
                state: null,
                dueTime: dueTime,
                period: interval
            );
        }


        private int _isExecuting = 0; // 0 = 空闲, 1 = 正在执行
        private void OnTimerElapsed(object state)
        {
            // ✅ 原子操作：尝试设置“正在执行”
            if (Interlocked.CompareExchange(ref _isExecuting, 1, 0) == 0)
            {
                try
                {
                    // ✅ 只有获得锁的线程才能执行
                    // 这个方法运行在**线程池线程**上
                    AlarmMonitor.Read();
                    TipsMonitor.Read();
                    MachineStateMonitor.Read();
                }
                finally
                {
                    // ✅ 释放：设置为 0
                    Interlocked.Exchange(ref _isExecuting, 0);
                }
            }
        }
        private void StopAlarmTipsMonitor()
        {
            if (_timerMonitor != null)
            {
                _timerMonitor.Dispose(); // 或 _timerMonitor.Change(Timeout.Infinite, 0);
                _timerMonitor = null;
            }
            AlarmMonitor.OnDataTriggered -= AlarmMonitor_OnDataTriggered;
            AlarmMonitor.OnDataCleared -= AlarmMonitor_OnDataCleared;
        }

        public bool RefreshInstanceFromPLC(IPlcAddressToPropertyPaths instance)
        {
            PlcDataMonitor monitor = new PlcDataMonitor();
            monitor.Protocol = Protocol;

            monitor.ReadNodes = instance.PlcAddressPropertyPathsPairs
                                .Select(n => new Node(n.Key))
                                .ToList();

            var results = monitor.Read();

            if (results != null)
            {
                foreach (var kvp in monitor.NodeStringValuePairs)
                {
                    //if (batch.PlcAddressPropertyPathPairs.TryGetValue(kvp.Key, out string propertyPath))
                    //{
                    //    ObjectPropertySetter.SetPropertyValue(batch, propertyPath, kvp.Value);
                    //}
                    if (instance.PlcAddressPropertyPathsPairs.TryGetValue(kvp.Key, out List<string> propertyPaths))
                    {
                        foreach (var propertyPath in propertyPaths)
                        {
                            ObjectPropertySetter.SetPropertyValue(instance, propertyPath, kvp.Value);
                        }
                    }
                }
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}
