using RobotNet.RobotShares.VDA5050.State;

namespace RobotNet.RobotManager.Services.OpenACS;

public class OpenACSPublisher(IConfiguration configuration,
    LoggerController<OpenACSPublisher> Logger,
    RobotManager RobotManager,
    OpenACSManager OpenACSManager) : BackgroundService
{
    public int PublishCount { get; private set; }
    private WatchTimerAsync<OpenACSPublisher>? Timer;
    private readonly string ACSSiteCode = configuration["ACSStatusConfig:SiteCode"] ?? "VN03";
    private readonly string ACSAreaCode = configuration["ACSStatusConfig:AreaCode"] ?? "DA3_FL1";
    private readonly string ACSAreaName = configuration["ACSStatusConfig:AreaName"] ?? "DA3_WM";
    private readonly double ACSExtendX = configuration.GetValue<double>("ACSStatusConfig:ExtendX");
    private readonly double ACSExtendY = configuration.GetValue<double>("ACSStatusConfig:ExtendY");
    private readonly double ACSExtendTheta = configuration.GetValue<double>("ACSStatusConfig:ExtendTheta");

    private async Task TimerHandler()
    {
        if (OpenACSManager.PublishEnable && !string.IsNullOrEmpty(OpenACSManager.PublishURL))
        {
            try
            {
                using var HttpClient = new HttpClient() { Timeout = TimeSpan.FromSeconds(15) };
                var robotIds = RobotManager.RobotSerialNumbers.ToArray();
                foreach (var robotId in robotIds)
                {
                    var startTime = DateTime.Now;
                    var robot = RobotManager[robotId];
                    if (robot == null || robot.StateMsg == null || robot.StateMsg.AgvPosition == null) continue;
                    if (!DateTime.TryParse(robot.StateMsg.Timestamp, out DateTime lastTimeUpdate)) continue;
                    if ((startTime - lastTimeUpdate).TotalMilliseconds > OpenACSManager.PublishInterval) continue;

                    int batLevel = (int)robot.StateMsg.BatteryState.BatteryHealth;
                    if (batLevel <= 0) batLevel = 85;

                    int batVol = (int)robot.StateMsg.BatteryState.BatteryVoltage;
                    if (batVol <= 0) batVol = 24;
                    var status = new RobotPublishStatusV2()
                    {
                        Header = new("AGV_STATUS", lastTimeUpdate.ToString("yyyy-MM-dd HH:mm:ss.fff")),
                        Body = new()
                        {
                            Id = robot.SerialNumber,
                            Location = new()
                            {
                                X = (robot.StateMsg.AgvPosition.X + ACSExtendX).ToString(),
                                Y = (robot.StateMsg.AgvPosition.Y + ACSExtendY).ToString(),
                                Z = "0",
                                Direction = (robot.StateMsg.AgvPosition.Theta + ACSExtendTheta).ToString(),
                            },
                            SiteCode = ACSSiteCode,
                            AreaCode = ACSAreaCode,
                            AreaName = ACSAreaName,
                            MarkerId = string.IsNullOrEmpty(robot.StateMsg.LastNodeId) ? null : robot.StateMsg.LastNodeId,
                            BatteryId = null,
                            BatteryLevel = batLevel.ToString(),
                            BatteryVoltage = batVol.ToString(),
                            BatterySOH = null,
                            BatteryCurrent = "1.0",
                            BatteryTemprature = "30",
                            StationId = null,
                            Loading = robot.StateMsg.Loads.Length != 0 ? "1" : "0",
                            ErrorCode = GetErrorCode(robot.StateMsg.Errors ?? [])?.ToString() ?? null,
                            State = GetStatus(robot.StateMsg).ToString(),
                        }
                    };

                    var response = await HttpClient.PostAsJsonAsync(OpenACSManager.PublishURL, status);
                    if (response.IsSuccessStatusCode)
                    {
                        var result = await response.Content.ReadFromJsonAsync<ACSStatusResponse>();
                        if (result == null)
                        {
                            Logger.Error("Failed to convert response.Content to ACSStatusResponse");
                        }
                        else if (result.Header.MessageName == "AGV_STATUS_ACK" && result.Body.Result == "OK")
                        {
                            PublishCount++;
                        }
                        else
                        {
                            Logger.Warning($"ACS response is not OK: {System.Text.Json.JsonSerializer.Serialize(result)}");
                        }
                    }
                    else
                    {
                        Logger.Warning($"Quá trình xuất bản tới {OpenACSManager.PublishURL} không thành công: {response.StatusCode}");
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Warning($"Quá trình xuất bản tới {OpenACSManager.PublishURL} có lỗi xảy ra: {ex.Message}");
            }
        }
    }

    private static int GetStatus(StateMsg state)
    {
        if (GetError(state) == ErrorLevel.FATAL || GetError(state) == ErrorLevel.WARNING) return (int)AGVState.Error;
        else if (state.BatteryState.Charging) return (int)AGVState.Charging;
        else if (state.Paused) return (int)AGVState.Pause;
        else if (IsIdle(state)) return (int)AGVState.Idle;
        else if (IsWorking(state)) return (int)AGVState.Run;
        else return (int)AGVState.Stop;
    }

    private static string? GetErrorCode(Error[] errors)
    {
        var error = errors.FirstOrDefault();
        if (error is not null && int.TryParse(error.ErrorType, out int errorCode)) return errorCode.ToString();
        return null;
    }

    private static bool IsIdle(StateMsg state)
    {
        if (state.NodeStates.Length != 0 || state.EdgeStates.Length != 0) return false;
        return true;
    }

    private static bool IsWorking(StateMsg state)
    {
        if (state.NodeStates.Length != 0 || state.EdgeStates.Length != 0) return true;
        return false;
    }

    private static ErrorLevel GetError(StateMsg state)
    {
        if (state.Errors is not null)
        {
            if (state.Errors.Any(error => error.ErrorLevel == ErrorLevel.FATAL.ToString())) return ErrorLevel.FATAL;
            if (state.Errors.Any(error => error.ErrorLevel == ErrorLevel.WARNING.ToString())) return ErrorLevel.WARNING;
        }
        return ErrorLevel.NONE;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await Task.Yield();
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                OpenACSManager.PublishIntervalChanged += UpdateInterval;
                Timer = new(OpenACSManager.PublishInterval <= 500 ? 500 : OpenACSManager.PublishInterval, TimerHandler, Logger);
                Timer.Start();
                break;
            }
            catch (Exception ex)
            {
                Logger.Warning($"Publisher ACS: Quá trình khởi tạo có lỗi xảy ra: {ex.Message}");
                await Task.Delay(2000, stoppingToken);
            }
        }
    }

    public void UpdateInterval()
    {
        Timer?.Dispose();
        Timer = new(OpenACSManager.PublishInterval <= 500 ? 500 : OpenACSManager.PublishInterval, TimerHandler, Logger);
        Timer.Start();
    }

    public override Task StopAsync(CancellationToken cancellationToken)
    {
        Timer?.Dispose();
        return Task.CompletedTask;
    }
}
