using Microsoft.AspNetCore.SignalR;
using RobotNet.Script.Shares;
using RobotNet.ScriptManager.Helpers;
using RobotNet.ScriptManager.Hubs;
using RobotNet.ScriptManager.Models;
using System.Diagnostics;

namespace RobotNet.ScriptManager.Services;

public class ScriptStateManager(ScriptTaskManager taskManager, 
                                    ScriptMissionManager missionManager,
                                    ScriptGlobalsManager globalsManager,
                                    ScriptConnectionManager connectionManager,
                                    IHubContext<ProcessorHub> processorHubContext,
                                    IHubContext<HMIHub> hmiHubContext,
                                    IHubContext<ConsoleHub> consoleHubContext, 
                                    ILogger<ScriptStateManager> logger) : LoopService(500)
{
    public string StateMesssage { get; private set; } = "";
    public ProcessorState State { get; private set; } = ProcessorState.Idle;
    public ProcessorRequest Request { get; private set; } = ProcessorRequest.None;
    private readonly ConsoleLog consoleLog = new(consoleHubContext, logger);

    private readonly Mutex mutex = new();
    private CancellationTokenSource? runningCancellation;

    public bool Build(ref string message)
    {
        bool result = false;

        if (mutex.WaitOne(1000))
        {
            if (Request != ProcessorRequest.None)
            {
                message = $"Không thể thực hiện build vì Processor đang thực hiện {Request}";
            }
            else if (State == ProcessorState.Running)
            {
                message = "Không thể thực hiện build vì Processor đang Running}";
            }
            else
            {
                result = true;
                SetRequest(ProcessorRequest.Build);
            }

            mutex.ReleaseMutex();
        }
        else
        {
            message = "Không thể thực hiện build vì request timeout";
        }

        return result;
    }

    public bool Run(ref string message)
    {
        bool result = false;

        if (mutex.WaitOne(1000))
        {
            if (Request != ProcessorRequest.None)
            {
                message = $"Không thể thực hiện run vì Processor đang thực hiện {Request}";
            }
            else if (State != ProcessorState.Ready)
            {
                message = $"Không thể thực hiện run vì Processor đang ở trạng thái {State}, không phải Ready";
            }
            else
            {
                result = true;
                SetRequest(ProcessorRequest.Run);
            }

            mutex.ReleaseMutex();
        }
        else
        {
            message = "Không thể thực hiện run vì request timeout";
        }

        return result;
    }

    public bool Stop(ref string message)
    {
        bool result = false;

        if (mutex.WaitOne(1000))
        {
            if (Request != ProcessorRequest.None)
            {
                message = $"Không thể thực hiện stop vì Processor đang thực hiện {Request}";
            }
            else if (State != ProcessorState.Running)
            {
                message = $"Không thể thực hiện stop vì Processor đang ở trạng thái {State}, không phải Running";
            }
            else
            {
                result = true;
                SetRequest(ProcessorRequest.Stop);
            }

            mutex.ReleaseMutex();
        }
        else
        {
            message = "Không thể thực hiện stop vì request timeout";
        }

        return result;
    }

    public bool Reset(ref string message)
    {
        bool result = false;

        if (mutex.WaitOne(1000))
        {
            if (Request != ProcessorRequest.None)
            {
                message = $"Không thể thực hiện reset vì Processor đang thực hiện {Request}";
            }
            else if (State != ProcessorState.Ready && State != ProcessorState.Error && State != ProcessorState.BuildError && State != ProcessorState.Idle)
            {
                message = $"Không thể thực hiện reset vì Processor đang ở trạng thái {State}, không phải Ready hoặc Error hoặc BuildError";
            }
            else
            {
                result = true;
                SetRequest(ProcessorRequest.Reset);
            }

            mutex.ReleaseMutex();
        }
        else
        {
            message = "Không thể thực hiện reset vì request timeout";
        }

        return result;
    }

    private void SetRequest(ProcessorRequest request)
    {
        if (Request == request) return;

        Request = request;
        _ = Task.Factory.StartNew(async () =>
        {
            await processorHubContext.Clients.All.SendAsync("RequestChanged", Request);
            await hmiHubContext.Clients.All.SendAsync("RequestChanged", Request);
        });
    }

    private void SetState(ProcessorState state)
    {
        if (State == state) return;

        State = state;
        _ = Task.Factory.StartNew(async () =>
        {
            await processorHubContext.Clients.All.SendAsync("StateChanged", State);
            await hmiHubContext.Clients.All.SendAsync("StateChanged", State);
        });
    }

    protected override async Task BeforExecuteAsync(CancellationToken cancellationToken)
    {
        missionManager.ResetMissionDb();
        SetRequest(ProcessorRequest.Build);
        await base.BeforExecuteAsync(cancellationToken);
    }

    protected override void Execute(CancellationToken stoppingToken)
    {
        switch (State)
        {
            case ProcessorState.Idle:
            case ProcessorState.BuildError:
            case ProcessorState.Error:
                if (Request == ProcessorRequest.Build)
                {
                    SetState(ProcessorState.Building);
                    SetRequest(ProcessorRequest.None);
                    BuildHandler();
                }
                else if (Request == ProcessorRequest.Reset)
                {
                    missionManager.Reset();
                    taskManager.Reset();
                    connectionManager.Reset();
                    SetState(ProcessorState.Idle);
                    SetRequest(ProcessorRequest.None);
                }
                break;
            case ProcessorState.Ready:
                if (Request == ProcessorRequest.Build)
                {
                    SetState(ProcessorState.Building);
                    SetRequest(ProcessorRequest.None);
                    BuildHandler();
                }
                else if (Request == ProcessorRequest.Run)
                {
                    SetState(ProcessorState.Running);
                    SetRequest(ProcessorRequest.None);
                    RunHandler();
                }
                else if (Request == ProcessorRequest.Reset)
                {
                    missionManager.Reset();
                    taskManager.Reset();
                    connectionManager.Reset();
                    SetState(ProcessorState.Idle);
                    SetRequest(ProcessorRequest.None);
                }
                break;
            case ProcessorState.Running:
                if (Request == ProcessorRequest.Stop)
                {
                    connectionManager.Reset();
                    SetState(ProcessorState.Stopping);
                    SetRequest(ProcessorRequest.None);
                    StopHandler();
                }
                break;
            case ProcessorState.Building:
            case ProcessorState.Stopping:
            case ProcessorState.Starting:
            default:
                SetRequest(ProcessorRequest.None);
                break;
        }
    }

    private void BuildHandler()
    {
        _ = Task.Factory.StartNew(() =>
        {
            consoleLog.LogInfo($"Start build all scripts");
            var watch = new Stopwatch();
            watch.Start();
            var code = ScriptConfiguration.GetScriptCode();

            string error = string.Empty;
            try
            {
                if (CSharpSyntaxHelper.VerifyScript(code, out error, out var variables, out var tasks, out var missions))
                {
                    globalsManager.LoadVariables(variables);
                    taskManager.LoadTasks(tasks);
                    missionManager.LoadMissions(missions);
                    watch.Stop();
                    consoleLog.LogInfo($"Build all scripts successfully in {watch.ElapsedMilliseconds} ms");
                    SetState(ProcessorState.Ready);
                    return;
                }
            }
            catch (Exception ex)
            {
                error = ex.ToString();
            }

            watch.Stop();
            SetState(ProcessorState.BuildError);
            consoleLog.LogError(error);

        });
    }

    private void RunHandler()
    {
        _ = Task.Factory.StartNew(() =>
        {
            runningCancellation = new();
            taskManager.StartAll(runningCancellation.Token);
            missionManager.Start(runningCancellation.Token);
            SetState(ProcessorState.Running);

        });
    }

    private void StopHandler()
    {
        _ = Task.Factory.StartNew(() =>
        {
            runningCancellation?.Cancel();
            taskManager.StopAll();
            missionManager.Stop();
            SetState(ProcessorState.Ready);
        });
    }

}
