using OpenIddict.Client; using RobotNet.MapShares.Dtos; using RobotNet.MapShares.Models; using RobotNet.Script; using RobotNet.Shares; using Serialize.Linq.Serializers; using System.Linq.Expressions; using System.Net.Http.Headers; namespace RobotNet.ScriptManager.Models; public class ScriptMapManager(OpenIddictClientService openIddictClient, string MapManagerUrl, string[] MapManagerScopes) : IMapManager { private string? CachedToken; private DateTime TokenExpiry; private static readonly ExpressionSerializer expressionSerializer = new(new Serialize.Linq.Serializers.JsonSerializer()); public Task FindElements(string map, string model) => FindElements(map, model, element => true); public Task FindElements(string map, string model, Expression> expr) => FindElements(map, model, expr, true); private async Task FindElements(string map, string model, Expression> expr, bool retry) { var accessToken = await RequestAccessToken(); using var client = new HttpClient() { BaseAddress = new Uri(MapManagerUrl) }; using var request = new HttpRequestMessage(HttpMethod.Post, $"api/ScriptElements"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); // Đưa modelExpression vào body của request request.Content = JsonContent.Create(new ElementExpressionModel { MapName = map, ModelName = model, Expression = expressionSerializer.SerializeText(expr), }); using var response = await client.SendAsync(request); using var status = response.EnsureSuccessStatusCode(); if (status.StatusCode == System.Net.HttpStatusCode.Unauthorized) { if (retry) { CachedToken = null; // Clear cached token to force re-authentication return await FindElements(map, model, expr, false); // Retry without recall } else { throw new UnauthorizedAccessException("Access token is invalid or expired. Please re-authenticate."); } } else if (status.StatusCode != System.Net.HttpStatusCode.OK) { throw new HttpRequestException($"Failed to get elements: {status.ReasonPhrase}"); } else { var result = await response.Content.ReadFromJsonAsync>>(); if (result == null) { throw new InvalidOperationException("Failed to deserialize response from server"); } else if (!result.IsSuccess) { throw new InvalidOperationException($"Error from server: {result.Message}"); } else if (result.Data == null || !result.Data.Any()) { return []; } return [.. result.Data.Select(e => new ScriptMapElement(map, e, UpdateIsOpenAsync, UpdatePropertiesAsync))]; } } public Task GetElement(string map, string name) => GetElement(map, name, true); private async Task GetElement(string map, string name, bool retry) { if (string.IsNullOrEmpty(map) || string.IsNullOrEmpty(name)) { throw new ArgumentException("Map name and element name cannot be null or empty"); } var accessToken = await RequestAccessToken(); using var client = new HttpClient() { BaseAddress = new Uri(MapManagerUrl) }; using var request = new HttpRequestMessage(HttpMethod.Get, $"api/ScriptElements/{map}/element/{name}"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); using var response = await client.SendAsync(request); using var status = response.EnsureSuccessStatusCode(); if (status.StatusCode == System.Net.HttpStatusCode.Unauthorized) { if (retry) { CachedToken = null; // Clear cached token to force re-authentication return await GetElement(map, name, false); // Retry without recall } else { throw new UnauthorizedAccessException("Access token is invalid or expired. Please re-authenticate."); } } else if (status.StatusCode != System.Net.HttpStatusCode.OK) { throw new HttpRequestException($"Failed to get element: {status.ReasonPhrase}"); } else { var result = await response.Content.ReadFromJsonAsync>(); if (result == null) { throw new InvalidOperationException("Failed to deserialize response from server"); } else if (!result.IsSuccess) { throw new InvalidOperationException($"Error from server: {result.Message}"); } else if (result.Data == null) { throw new KeyNotFoundException("Element not found in response"); } else { return new ScriptMapElement(map, result.Data, UpdateIsOpenAsync, UpdatePropertiesAsync); } } } private Task UpdateIsOpenAsync(string mapName, string elementName, bool isOpen) => UpdateIsOpenAsync(mapName, elementName, isOpen, true); private async Task UpdateIsOpenAsync(string mapName, string elementName, bool isOpen, bool retry) { var accessToken = await RequestAccessToken(); using var client = new HttpClient() { BaseAddress = new Uri(MapManagerUrl) }; using var request = new HttpRequestMessage(HttpMethod.Patch, $"api/ScriptElements/{mapName}/element/{elementName}/IsOpen?isOpen={isOpen}"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); //request.Content = JsonContent.Create(new { isOpen }); using var response = await client.SendAsync(request); using var status = response.EnsureSuccessStatusCode(); if (status.StatusCode == System.Net.HttpStatusCode.Unauthorized) { if (retry) { CachedToken = null; // Clear cached token to force re-authentication await UpdateIsOpenAsync(mapName, elementName, isOpen, false); // Retry without recall } else { throw new UnauthorizedAccessException("Access token is invalid or expired. Please re-authenticate."); } } else if (status.StatusCode != System.Net.HttpStatusCode.OK) { throw new HttpRequestException($"Failed to update IsOpen: {status.ReasonPhrase}"); } else { var result = await response.Content.ReadFromJsonAsync(); if (result == null) { throw new InvalidOperationException("Failed to deserialize response from server"); } else if (!result.IsSuccess) { throw new InvalidOperationException($"Error from server: {result.Message}"); } } } private Task UpdatePropertiesAsync(string mapName, string elementName, ElementPropertyUpdateModel model) => UpdatePropertiesAsync(mapName, elementName, model, true); private async Task UpdatePropertiesAsync(string mapName, string elementName, ElementPropertyUpdateModel model, bool retry) { var accessToken = await RequestAccessToken(); using var client = new HttpClient() { BaseAddress = new Uri(MapManagerUrl) }; using var request = new HttpRequestMessage(HttpMethod.Patch, $"api/ScriptElements/{mapName}/element/{elementName}"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); // Đưa modelExpression vào body của request request.Content = JsonContent.Create(model); using var response = await client.SendAsync(request); using var status = response.EnsureSuccessStatusCode(); if (status.StatusCode == System.Net.HttpStatusCode.Unauthorized) { if (retry) { CachedToken = null; // Clear cached token to force re-authentication await UpdatePropertiesAsync(mapName, elementName, model, false); // Retry without recall } else { throw new UnauthorizedAccessException("Access token is invalid or expired. Please re-authenticate."); } } else if (status.StatusCode != System.Net.HttpStatusCode.OK) { throw new HttpRequestException($"Failed to get elements: {status.ReasonPhrase}"); } else { var result = await response.Content.ReadFromJsonAsync(); if (result == null) { throw new InvalidOperationException("Failed to deserialize response from server"); } else if (!result.IsSuccess) { throw new InvalidOperationException($"Error from server: {result.Message}"); } } } public async Task RequestAccessToken() { try { if (!string.IsNullOrEmpty(CachedToken) && DateTime.UtcNow < TokenExpiry) { return CachedToken; } var result = await openIddictClient.AuthenticateWithClientCredentialsAsync(new() { Scopes = [.. MapManagerScopes], }); if (result == null || result.AccessToken == null || result.AccessTokenExpirationDate == null) { return null; } else { TokenExpiry = result.AccessTokenExpirationDate.Value.UtcDateTime; CachedToken = result.AccessToken; return CachedToken; } } catch { return null; } } public Task GetNode(string map, string name) => GetNode(map, name, true); private async Task GetNode(string map, string name, bool retry) { if (string.IsNullOrEmpty(map) || string.IsNullOrEmpty(name)) { throw new ArgumentException("Map name and element name cannot be null or empty"); } var accessToken = await RequestAccessToken(); using var client = new HttpClient() { BaseAddress = new Uri(MapManagerUrl) }; using var request = new HttpRequestMessage(HttpMethod.Get, $"api/ScriptElements/{map}/node/{name}"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); using var response = await client.SendAsync(request); using var status = response.EnsureSuccessStatusCode(); if (status.StatusCode == System.Net.HttpStatusCode.Unauthorized) { if (retry) { CachedToken = null; // Clear cached token to force re-authentication return await GetNode(map, name, false); // Retry without recall } else { throw new UnauthorizedAccessException("Access token is invalid or expired. Please re-authenticate."); } } else if (status.StatusCode != System.Net.HttpStatusCode.OK) { throw new HttpRequestException($"Failed to get element: {status.ReasonPhrase}"); } else { var result = await response.Content.ReadFromJsonAsync>(); if (result == null) { throw new InvalidOperationException("Failed to deserialize response from server"); } else if (!result.IsSuccess) { throw new InvalidOperationException($"Error from server: {result.Message}"); } else if (result.Data == null) { throw new KeyNotFoundException("Element not found in response"); } else { return new ScriptMapNode(result.Data.Id, result.Data.MapId, result.Data.Name, result.Data.X, result.Data.Y, result.Data.Theta); } } } }