using MainShell.Alarm;
using MainShell.Common;
using MainShell.Log;
using MainShell.Vision;
using MW.WorkFlow;
using Stylet;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace MainShell.Process
{
    public class ChipPreparationRequestHandler : IHandle<ChipPreparationRequestedEventArgs>, IHandle<ChipPreparationStateChangedEventArgs>, IDisposable
    {
        private readonly IEventAggregator _eventAggregator;
        private readonly PreparationAreaService _preparationAreaService;
        private readonly AlarmOperate _alarmOperate;
        private readonly WorkflowRunner _workflowRunner;
        private readonly SemaphoreSlim _serialGate = new SemaphoreSlim(1, 1);
        private readonly SemaphoreSlim _runnerStateGate = new SemaphoreSlim(1, 1);
        private readonly CancellationTokenSource _disposeCts = new CancellationTokenSource();
        private bool _disposed;

        public ChipPreparationRequestHandler(IEventAggregator eventAggregator, PreparationAreaService preparationAreaService, AlarmOperate alarmOperate)
        {
            _eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
            _preparationAreaService = preparationAreaService ?? throw new ArgumentNullException(nameof(preparationAreaService));
            _alarmOperate = alarmOperate ?? throw new ArgumentNullException(nameof(alarmOperate));
            _workflowRunner = new WorkflowRunner();

            _eventAggregator.Subscribe(this);
        }

        public void Handle(ChipPreparationRequestedEventArgs message)
        {
            if (message == null || message.Request == null)
            {
                return;
            }

            ChipPreparationRequest request = CloneRequest(message.Request);
            _ = ProcessRequestAsync(request);
        }

        public void Handle(ChipPreparationStateChangedEventArgs message)
        {
            if (message == null || message.Snapshot == null)
            {
                return;
            }

            ChipPreparationStateSnapshot snapshot = CloneSnapshot(message.Snapshot);
            _ = SynchronizeRunnerStateAsync(snapshot);
        }

        public void Dispose()
        {
            if (_disposed)
            {
                return;
            }

            _disposed = true;
            _disposeCts.Cancel();
            _eventAggregator.Unsubscribe(this);
            _workflowRunner.Dispose();
            _disposeCts.Dispose();
            _serialGate.Dispose();
            _runnerStateGate.Dispose();
        }

        private async Task ProcessRequestAsync(ChipPreparationRequest request)
        {
            bool gateEntered = false;
            try
            {
                await _serialGate.WaitAsync(_disposeCts.Token);
                gateEntered = true;

                string.Format("Chip preparation request started. {0}.", BuildRequestSummary(request)).LogProcessInfo();

                ChipPreparationWaitResult executeResult = await ExecuteOnDemandActivityAsync(request);
                string rejectReason;

                if (executeResult.Success)
                {
                    _preparationAreaService.TryMarkReady(request.RequestId, out rejectReason);
                }
                else
                {
                    string errorMessage = string.IsNullOrWhiteSpace(executeResult.ErrorMessage) ? "芯片准备执行失败。" : executeResult.ErrorMessage;
                    _preparationAreaService.TryMarkFault(errorMessage, out rejectReason);
                    int? visionAlarmId = ResolveVisionAlarmId(executeResult);
                    if (visionAlarmId.HasValue)
                    {
                        await _alarmOperate.AlertAsync(
                            visionAlarmId.Value,
                            nameof(ChipPreparationRequestHandler),
                            "Vision alarm triggered",
                            BuildAlarmTriggerSummary(request, executeResult)).ConfigureAwait(false);
                    }
                }
            }
            catch (OperationCanceledException)
            {
            }
            catch (Exception ex)
            {
                string.Format("Chip preparation request failed with exception. {0}. {1}", BuildRequestSummary(request), ex).LogProcessError();
                string rejectReason;
                _preparationAreaService.TryMarkFault("芯片准备执行异常: " + ex.Message, out rejectReason);
            }
            finally
            {
                if (gateEntered)
                {
                    _serialGate.Release();
                }
            }
        }

        private async Task<ChipPreparationWaitResult> ExecuteOnDemandActivityAsync(ChipPreparationRequest request)
        {
            try
            {
                WorkflowContext context = new WorkflowContext();
                context[WorkflowContextKeys.WorkflowName] = ProcessFlowName.ChipPreparationOnDemandFlow;

                string activityName = request != null && request.Action == ChipPreparationAction.Unload
                    ? ProcessFlowName.ChipUnloadExecuteActivity
                    : ProcessFlowName.ChipLoadExecuteActivity;
                ChipPreparationExecuteActivity activity = new ChipPreparationExecuteActivity(activityName, request);

                WorkflowRunCompletedEventArgs runResult = await _workflowRunner.RunActivityWithResultAsync(activity, context);
                if (runResult == null)
                {
                    string.Format("Chip preparation request returned no workflow result. {0}.", BuildRequestSummary(request)).LogProcessError();
                    return ChipPreparationWaitResult.CreateFailure("芯片准备流程未返回执行结果。");
                }

                switch (runResult.FinalState)
                {
                    case WorkflowState.Completed:
                        string.Format("Chip preparation request completed. {0}, FinalState={1}.", BuildRequestSummary(request), runResult.FinalState).LogProcessInfo();
                        return ChipPreparationWaitResult.CreateSuccess();
                    case WorkflowState.Canceled:
                        string.Format("Chip preparation request canceled. {0}, FinalState={1}, ErrorMessage={2}.", BuildRequestSummary(request), runResult.FinalState, NormalizeLogValue(runResult.Error)).LogProcessError();
                        return ChipPreparationWaitResult.CreateCanceled("芯片准备流程已取消。");
                    case WorkflowState.Faulted:
                        string.Format("Chip preparation request faulted. {0}, FinalState={1}, ErrorMessage={2}.", BuildRequestSummary(request), runResult.FinalState, NormalizeLogValue(runResult.Error)).LogProcessError();
                        return ChipPreparationWaitResult.CreateFailure(string.IsNullOrWhiteSpace(runResult.Error)
                            ? "芯片准备流程执行失败。"
                            : runResult.Error);
                    default:
                        string.Format("Chip preparation request returned unexpected state. {0}, FinalState={1}, ErrorMessage={2}.", BuildRequestSummary(request), runResult.FinalState, NormalizeLogValue(runResult.Error)).LogProcessError();
                        return ChipPreparationWaitResult.CreateFailure(string.Format("芯片准备流程返回了未预期状态: {0}。", runResult.FinalState));
                }
            }
            catch (Exception ex)
            {
                string.Format("Chip preparation workflow execution threw an exception. {0}. {1}", BuildRequestSummary(request), ex).LogProcessError();
                return ChipPreparationWaitResult.CreateFailure(ex.Message);
            }
        }

        private async Task SynchronizeRunnerStateAsync(ChipPreparationStateSnapshot snapshot)
        {
            if (snapshot == null || _disposed)
            {
                return;
            }

            await _runnerStateGate.WaitAsync(_disposeCts.Token).ConfigureAwait(false);
            try
            {
                if (snapshot.Status == ChipPreparationStatus.Paused)
                {
                    string.Format("Chip preparation runner paused. {0}.", BuildSnapshotSummary(snapshot)).LogProcessDebug();
                    _workflowRunner.Pause();
                    return;
                }

                if (snapshot.Status == ChipPreparationStatus.Preparing ||
                    snapshot.Status == ChipPreparationStatus.Loading ||
                    snapshot.Status == ChipPreparationStatus.Unloading)
                {
                    string.Format("Chip preparation runner resumed. {0}.", BuildSnapshotSummary(snapshot)).LogProcessDebug();
                    _workflowRunner.Resume();
                    return;
                }

                if ((snapshot.Status == ChipPreparationStatus.Idle || snapshot.Status == ChipPreparationStatus.Faulted) &&
                    _workflowRunner.IsRunning)
                {
                    string.Format("Chip preparation runner stopping. {0}.", BuildSnapshotSummary(snapshot)).LogProcessDebug();
                    await _workflowRunner.StopAsync().ConfigureAwait(false);
                }
            }
            catch (OperationCanceledException)
            {
            }
            finally
            {
                _runnerStateGate.Release();
            }
        }

        private static ChipPreparationRequest CloneRequest(ChipPreparationRequest request)
        {
            if (request == null)
            {
                return null;
            }

            return new ChipPreparationRequest
            {
                RequestId = request.RequestId,
                RecipeName = request.RecipeName,
                Action = request.Action,
                SourceStepId = request.SourceStepId
            };
        }

        private static ChipPreparationStateSnapshot CloneSnapshot(ChipPreparationStateSnapshot snapshot)
        {
            if (snapshot == null)
            {
                return null;
            }

            return new ChipPreparationStateSnapshot
            {
                Status = snapshot.Status,
                ActiveRequestId = snapshot.ActiveRequestId,
                RecipeName = snapshot.RecipeName,
                SourceStepId = snapshot.SourceStepId,
                CurrentStage = snapshot.CurrentStage,
                ErrorMessage = snapshot.ErrorMessage,
                LastUpdatedTime = snapshot.LastUpdatedTime,
                CurrentAction = snapshot.CurrentAction,
                IsChipLoaded = snapshot.IsChipLoaded
            };
        }
        private static int? ResolveVisionAlarmId(ChipPreparationWaitResult executeResult)
        {
            if (executeResult == null)
            {
                return null;
            }

            if (!string.IsNullOrWhiteSpace(executeResult.ErrorMessage) &&
                executeResult.ErrorMessage.IndexOf("Vision", StringComparison.OrdinalIgnoreCase) >= 0)
            {
                return VisionAlarmIds.TemplateMatchFailed;
            }

            return null;
        }

        private static string BuildRequestSummary(ChipPreparationRequest request)
        {
            if (request == null)
            {
                return "RequestId=N/A, Action=None, RecipeName=N/A, SourceStepId=N/A";
            }

            return string.Format(
                "RequestId={0}, Action={1}, RecipeName={2}, SourceStepId={3}",
                NormalizeLogValue(request.RequestId),
                request.Action,
                NormalizeLogValue(request.RecipeName),
                NormalizeLogValue(request.SourceStepId));
        }

        private static string BuildSnapshotSummary(ChipPreparationStateSnapshot snapshot)
        {
            if (snapshot == null)
            {
                return "Status=N/A, ActiveRequestId=N/A, CurrentAction=None, SourceStepId=N/A, IsChipLoaded=False";
            }

            return string.Format(
                "Status={0}, ActiveRequestId={1}, CurrentAction={2}, SourceStepId={3}, IsChipLoaded={4}",
                snapshot.Status,
                NormalizeLogValue(snapshot.ActiveRequestId),
                snapshot.CurrentAction,
                NormalizeLogValue(snapshot.SourceStepId),
                snapshot.IsChipLoaded);
        }

        private static string BuildAlarmTriggerSummary(ChipPreparationRequest request, ChipPreparationWaitResult executeResult)
        {
            return string.Format(
                "{0}, ErrorMessage={1}",
                BuildRequestSummary(request),
                executeResult == null ? "N/A" : NormalizeLogValue(executeResult.ErrorMessage));
        }

        private static string NormalizeLogValue(string value)
        {
            return string.IsNullOrWhiteSpace(value) ? "N/A" : value;
        }
    }
 
    public class ChipPreparationExecuteActivity : ActivityAbstractBase
    {
        private readonly ChipPreparationRequest _request;

        public ChipPreparationExecuteActivity(string name, ChipPreparationRequest request)
            : base(name)
        {
            _request = request;
        }

        protected override async Task<ActivityResult> OnExecuteAsync(WorkflowContext context, ActivityControl activityControl)
        {
            if (_request == null)
            {
                return ActivityResult.Failure;
            }

            for (int i = 0; i < 3; i++)
            {
                activityControl.ThrowIfCancellationRequested();
                await activityControl.CheckPauseAsync();
                await Task.Delay(300, activityControl.CancellationToken);
            }

            return ActivityResult.Success;
        }
    }
}
