using MaxwellFramework.Core.Attributes;
using MainShell.Common;
using MainShell.ProcessResult;
using MW.WorkFlow;
using System;
using System.Threading.Tasks;

namespace MainShell.Process
{
    [Singleton]
    public class DieTransferMotionService
    {
        private readonly AutoProductionRuntimeStateService _autoProductionRuntimeStateService;
        private readonly IDieTransferPathGenerator _dieTransferPathGenerator;

        public DieTransferMotionService(AutoProductionRuntimeStateService autoProductionRuntimeStateService, IDieTransferPathGenerator dieTransferPathGenerator)
        {
            _autoProductionRuntimeStateService = autoProductionRuntimeStateService ?? throw new ArgumentNullException(nameof(autoProductionRuntimeStateService));
            _dieTransferPathGenerator = dieTransferPathGenerator ?? throw new ArgumentNullException(nameof(dieTransferPathGenerator));
        }

        public async Task ExecuteAsync(WorkflowContext context, ActivityControl activityControl)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (activityControl == null)
            {
                throw new ArgumentNullException(nameof(activityControl));
            }

            TryGeneratePathPlan(context);

            for (var i = 0; i < 20; i++)
            {
                activityControl.ThrowIfCancellationRequested();
                await activityControl.CheckPauseAsync().ConfigureAwait(false);
                await Task.Delay(100, activityControl.CancellationToken).ConfigureAwait(false);
            }

            DieTransferDecisionResult decisionResult = ResolveDecisionResult(context);
            context.SetData(WorkflowContextKeys.DieTransferDecisionResult, decisionResult);
            context.SetRoute(WorkflowContextKeys.AutoProductionRoute, decisionResult.SuggestedRoute);

            bool hasCurrentChipStateService = context.TryGetData<CurrentChipStateService>(WorkflowContextKeys.CurrentChipStateService, out CurrentChipStateService currentChipStateService) && currentChipStateService != null;

            if (decisionResult.SuggestedRoute == AutoProductionRoute.ChipExhausted ||
                decisionResult.SuggestedRoute == AutoProductionRoute.BothExhausted)
            {
                context.SetData(WorkflowContextKeys.PendingChipLoad, true);
                context.SetData(WorkflowContextKeys.CurrentChipState, CurrentChipLifecycleState.PendingUnload);
                if (hasCurrentChipStateService)
                {
                    currentChipStateService.SetState(CurrentChipLifecycleState.PendingUnload);
                }
            }
            else
            {
                context.SetData(WorkflowContextKeys.PendingChipLoad, false);
                context.SetData(WorkflowContextKeys.CurrentChipState, CurrentChipLifecycleState.InUse);
                if (hasCurrentChipStateService)
                {
                    currentChipStateService.SetState(CurrentChipLifecycleState.InUse);
                }
            }

            if (decisionResult.IsSubstrateComplete)
            {
                context.SetData(WorkflowContextKeys.SubstrateProcessState, SubstrateLifecycleState.Complete);
            }
            else
            {
                context.SetData(WorkflowContextKeys.SubstrateProcessState, SubstrateLifecycleState.InProcess);
            }

            SyncRuntimeStateToContext(context);
        }

        public DieTransferPathPlan GeneratePathPlan(DieTransferPathRequest request)
        {
            return _dieTransferPathGenerator.Generate(request);
        }

        private DieTransferDecisionResult ResolveDecisionResult(WorkflowContext context)
        {
            bool needsRecheck = ConsumeBooleanFlag(context, WorkflowContextKeys.TransferNeedsRecheck) || _autoProductionRuntimeStateService.ConsumeTransferNeedsRecheck();
            bool explicitChipExhausted = ConsumeBooleanFlag(context, WorkflowContextKeys.TransferChipExhausted) || _autoProductionRuntimeStateService.ConsumeTransferChipExhausted();

            if (needsRecheck)
            {
                return CreateDecisionFromRoute(AutoProductionRoute.Recheck);
            }

            AutoProductionRoute autoRoute;
            if (context.TryGetRoute<AutoProductionRoute>(WorkflowContextKeys.AutoProductionRoute, out autoRoute))
            {
                if (autoRoute == AutoProductionRoute.Recheck)
                {
                    return CreateDecisionFromRoute(autoRoute);
                }
            }

            DieTransferRoute legacyRoute;
            if (context.TryGetRoute<DieTransferRoute>(WorkflowContextKeys.DieTransferRoute, out legacyRoute) &&
                legacyRoute == DieTransferRoute.Recheck)
            {
                return CreateDecisionFromRoute(AutoProductionRoute.Recheck);
            }

            bool isSubstrateComplete = UpdateSubstrateProgress(context);
            bool isChipExhausted = explicitChipExhausted || UpdateCurrentChipBudget(context);

            if (isSubstrateComplete && isChipExhausted)
            {
                return CreateDecisionFromRoute(AutoProductionRoute.BothExhausted);
            }

            if (isSubstrateComplete)
            {
                return CreateDecisionFromRoute(AutoProductionRoute.SubstrateComplete);
            }

            if (isChipExhausted)
            {
                return CreateDecisionFromRoute(AutoProductionRoute.ChipExhausted);
            }

            return CreateDecisionFromRoute(AutoProductionRoute.ContinueCurrentSubstrate);
        }

        private void TryGeneratePathPlan(WorkflowContext context)
        {
            DieTransferPathRequest pathRequest;
            if (!context.TryGetData<DieTransferPathRequest>(WorkflowContextKeys.DieTransferPathRequest, out pathRequest) || pathRequest == null)
            {
                return;
            }

            DieTransferPathPlan pathPlan = _dieTransferPathGenerator.Generate(pathRequest);
            context.SetData(WorkflowContextKeys.DieTransferPathPlan, pathPlan);
        }

        private bool UpdateSubstrateProgress(WorkflowContext context)
        {
            if (_autoProductionRuntimeStateService.CurrentSubstratePendingCount <= 0)
            {
                return false;
            }

            return _autoProductionRuntimeStateService.ConsumeSubstrateTarget();
        }

        private bool UpdateCurrentChipBudget(WorkflowContext context)
        {
            if (_autoProductionRuntimeStateService.CurrentChipRemainingCount <= 0)
            {
                return false;
            }

            return _autoProductionRuntimeStateService.ConsumeCurrentChip();
        }

        private static bool ConsumeBooleanFlag(WorkflowContext context, string key)
        {
            bool flagValue;
            if (!context.TryGetData<bool>(key, out flagValue) || !flagValue)
            {
                return false;
            }

            context.SetData(key, false);
            return true;
        }

        private void SyncRuntimeStateToContext(WorkflowContext context)
        {
            context.SetData(WorkflowContextKeys.CurrentSubstratePendingCount, _autoProductionRuntimeStateService.CurrentSubstratePendingCount);
            context.SetData(WorkflowContextKeys.CurrentSubstrateProcessedCount, _autoProductionRuntimeStateService.CurrentSubstrateProcessedCount);
            context.SetData(WorkflowContextKeys.CurrentChipRemainingCount, _autoProductionRuntimeStateService.CurrentChipRemainingCount);
            context.SetData(WorkflowContextKeys.TransferNeedsRecheck, _autoProductionRuntimeStateService.TransferNeedsRecheck);
            context.SetData(WorkflowContextKeys.TransferChipExhausted, _autoProductionRuntimeStateService.TransferChipExhausted);
        }

        private static DieTransferDecisionResult CreateDecisionFromRoute(AutoProductionRoute route)
        {
            DieTransferDecisionResult decisionResult = new DieTransferDecisionResult();
            decisionResult.SuggestedRoute = route;
            decisionResult.NeedsRecheck = route == AutoProductionRoute.Recheck;
            decisionResult.IsChipExhausted = route == AutoProductionRoute.ChipExhausted || route == AutoProductionRoute.BothExhausted;
            decisionResult.IsSubstrateComplete = route == AutoProductionRoute.SubstrateComplete || route == AutoProductionRoute.BothExhausted;
            return decisionResult;
        }
    }
}