using NLog.Targets;
using RobotNet.MapShares.Dtos;
using RobotNet.RobotShares.OpenACS;
using RobotNet.Shares;
using System.Text.Json;

namespace RobotNet.RobotManager.Services.OpenACS;

public class TrafficACS(OpenACSManager OpenACSManager, IConfiguration Configuration, LoggerController<TrafficACS> Logger)
{
    public bool Enable => OpenACSManager.TrafficEnable;
    private readonly double TrafficCheckingDistanceMin = Configuration.GetValue("TrafficConfig:CheckingDistanceMin", 3);
    public readonly double DeviationDistance = Configuration.GetValue("TrafficConfig:DeviationDistance", 0.5);
    private static readonly JsonSerializerOptions jsonSerializeOptions = new() { 
        WriteIndented = true,
        Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
    };

    public async Task<MessageResult<bool>> RequestIn(string robotId, string zoneId)
    {
        try
        {
            if (!OpenACSManager.TrafficEnable) return new(true, "Kết nối với hệ thống traffic ACS không được bật") { Data = true };
            using var HttpClient = new HttpClient() { Timeout = TimeSpan.FromSeconds(15) };

            var model = new TrafficACSRequestV2(robotId, zoneId, TrafficRequestType.IN.ToInOutString());

            var response = await (await HttpClient.PostAsJsonAsync(OpenACSManager.TrafficURL, model)).Content.ReadFromJsonAsync<TrafficACSResponseV2>() ??
                throw new OpenACSException("Lỗi giao tiếp với hệ thống traffic ACS");
            if (response.Body.AgvId != robotId) throw new OpenACSException($"Dữ liệu hệ thống traffic ACS agv_id trả về {response.Body.AgvId} không trùng với dữ liệu gửi đi {robotId}");
            if (response.Body.TrafficZoneId != zoneId) throw new OpenACSException($"Dữ liệu hệ thống traffic ACS traffic_zone_id trả về {response.Body.TrafficZoneId} không trùng với dữ liệu gửi đi {zoneId}");
            if (response.Body.InOut != TrafficRequestType.IN.ToInOutString()) throw new OpenACSException($"Dữ liệu hệ thống traffic ACS inout trả về {response.Body.InOut} không trùng với dữ liệu gửi đi in");
            Logger.Info($"{robotId} yêu cầu vào traffic zone {zoneId} \nRequest: {JsonSerializer.Serialize(model, jsonSerializeOptions)}\n trả về kết quả: {JsonSerializer.Serialize(response, jsonSerializeOptions)}");
            return new(true, "Yêu cầu vào traffic zone thành công") { Data = response.Body.Result == TrafficACSResult.GO };
        }
        catch (OpenACSException ex)
        {
            Logger.Warning($"{robotId} request in xảy ra lỗi: {ex.Message}");
            return new(false, ex.Message);
        }
        catch (Exception ex)
        {
            Logger.Warning($"{robotId} request In xảy ra lỗi: {ex.Message}");
            return new(false, "Lỗi giao tiếp với hệ thống traffic ACS");
        }
    }

    public async Task<MessageResult<bool>> RequestOut(string robotId, string zoneId)
    {
        try
        {
            if (!OpenACSManager.TrafficEnable) return new(true, "Kết nối với hệ thống traffic ACS không được bật") { Data = true};
            using var HttpClient = new HttpClient() { Timeout = TimeSpan.FromSeconds(15) };
            var model = new TrafficACSRequestV2(robotId, zoneId, TrafficRequestType.OUT.ToInOutString());
            var response = await (await HttpClient.PostAsJsonAsync(OpenACSManager.TrafficURL, model)).Content.ReadFromJsonAsync<TrafficACSResponseV2>() ??
                throw new OpenACSException("Lỗi giao tiếp với hệ thống traffic ACS");
            if (response.Body.AgvId != robotId) throw new OpenACSException($"Dữ liệu hệ thống traffic ACS agv_id trả về {response.Body.AgvId} không trùng với dữ liệu gửi đi {robotId}");
            if (response.Body.TrafficZoneId != zoneId) throw new OpenACSException($"Dữ liệu hệ thống traffic ACS traffic_zone_id trả về {response.Body.TrafficZoneId} không trùng với dữ liệu gửi đi {zoneId}");
            if (response.Body.InOut != TrafficRequestType.OUT.ToInOutString()) throw new OpenACSException($"Dữ liệu hệ thống traffic ACS inout trả về {response.Body.InOut} không trùng với dữ liệu gửi đi out");
            Logger.Info($"{robotId} yêu cầu ra khỏi traffic zone {zoneId}  \nRequest: {JsonSerializer.Serialize(model, jsonSerializeOptions)}\n trả về kết quả: {JsonSerializer.Serialize(response, jsonSerializeOptions)}");
            return new(true, "Yêu cầu ra khỏi traffic zone thành công") { Data = response.Body.Result == TrafficACSResult.GO };
        }
        catch (OpenACSException ex)
        {
            Logger.Warning($"{robotId} request out xảy ra lỗi: {ex.Message}");
            return new(false, ex.Message);
        }
        catch (Exception ex)
        {
            Logger.Warning($"{robotId} request Out xảy ra lỗi: {ex.Message}");
            return new(false, "Lỗi giao tiếp với hệ thống traffic ACS");
        }
    }

    public Dictionary<Guid, ZoneDto[]> GetZones(Guid inNodeId, NodeDto[] nodes, Dictionary<Guid, ZoneDto[]> zones)
    {
        int inNodeIndex = Array.FindIndex(nodes, n => n.Id == inNodeId);
        if (inNodeId == Guid.Empty || (inNodeIndex != -1 && inNodeIndex < nodes.Length - 1))
        {
            List<NodeDto> baseNodes = [];
            List<ZoneDto> basezones = [];
            double distance = 0;
            int index = inNodeIndex != -1 ? inNodeIndex + 1 : 1;
            for (; index < nodes.Length; index++)
            {
                baseNodes.Add(nodes[index]);
                distance += Math.Sqrt(Math.Pow(nodes[index].X - nodes[index - 1].X, 2) + Math.Pow(nodes[index].Y - nodes[index - 1].Y, 2));
                if (distance > TrafficCheckingDistanceMin) break;
            }

            Dictionary<Guid, ZoneDto[]> nodeZones = [];

            foreach (var node in baseNodes)
            {
                if (zones.TryGetValue(node.Id, out ZoneDto[]? zone) && zone is not null && zone.Length > 0) nodeZones.Add(node.Id, zone);
                else nodeZones.Add(node.Id, []);
            }

            return nodeZones;
        }
        else if (inNodeIndex == nodes.Length - 1) return [];
        else
        {
            Logger.Warning($"Không tìm thấy node {inNodeId} trong danh sách nodes hoặc node này.");
            return [];
        }
    }
}
