using Microsoft.AspNetCore.Components.Authorization; using RobotNet10.CustomConfigurationEditor.Models; using RobotNet10.CustomConfigurationEditor.Services.API; using System.Net.Http; using System.Security.Claims; using System.Threading; namespace RobotNet10.CustomConfigurationEditor.Services.State; /// /// State management cho Config Manager /// public class ConfigManagerState(ConfigApiService apiService, AuthenticationStateProvider? authStateProvider = null, string editorRole = "") { private readonly ConfigApiService _apiService = apiService; private readonly AuthenticationStateProvider? _authStateProvider = authStateProvider; private readonly SemaphoreSlim _stateLock = new(1, 1); // ===== DATA ===== public List Configs { get; private set; } = []; public ConfigFileModel? SelectedConfig { get; private set; } // ===== FILTERS & SEARCH ===== public string? SearchQuery { get; set; } // ===== UI STATE ===== public bool IsLoading { get; private set; } public bool IsSaving { get; private set; } public string? ErrorMessage { get; private set; } // ===== EVENTS ===== public event Action? OnStateChanged; // ========================================== // PUBLIC METHODS // ========================================== // Role Editor public string EditorRole { get; } = editorRole; /// /// Load tất cả configs /// public async Task LoadConfigsAsync(string? searchQuery = null) { await _stateLock.WaitAsync(); try { IsLoading = true; SearchQuery = searchQuery; ErrorMessage = null; NotifyStateChanged(); try { Configs = await _apiService.GetAllConfigsAsync(searchQuery); } catch (Exception ex) { ErrorMessage = HttpErrorHelper.GetErrorMessage(ex); Configs = []; } IsLoading = false; NotifyStateChanged(); } finally { _stateLock.Release(); } } /// /// Load config theo ID /// public async Task LoadConfigByIdAsync(Guid id) { await _stateLock.WaitAsync(); try { IsLoading = true; ErrorMessage = null; NotifyStateChanged(); try { SelectedConfig = await _apiService.GetConfigByIdAsync(id); if (SelectedConfig == null) { ErrorMessage = "Config not found"; } } catch (Exception ex) { ErrorMessage = HttpErrorHelper.GetErrorMessage(ex); SelectedConfig = null; } IsLoading = false; NotifyStateChanged(); } finally { _stateLock.Release(); } } /// /// Load config theo ConfigType /// public async Task LoadConfigByTypeAsync(string configType) { await _stateLock.WaitAsync(); try { IsLoading = true; ErrorMessage = null; NotifyStateChanged(); try { SelectedConfig = await _apiService.GetConfigByTypeAsync(configType); if (SelectedConfig == null) { ErrorMessage = "Config not found"; } } catch (Exception ex) { ErrorMessage = HttpErrorHelper.GetErrorMessage(ex); SelectedConfig = null; } IsLoading = false; NotifyStateChanged(); } finally { _stateLock.Release(); } } /// /// Select config /// public async Task SelectConfigAsync(ConfigFileMetadataModel configMetadata) { await LoadConfigByIdAsync(configMetadata.Id); } /// /// Clear error message /// public void ClearError() { ErrorMessage = null; NotifyStateChanged(); } /// /// Clear selection /// public void ClearSelection() { SelectedConfig = null; NotifyStateChanged(); } /// /// Tạo config mới /// public async Task CreateConfigAsync(string configType, List variables, string? description = null) { await _stateLock.WaitAsync(); try { IsSaving = true; ErrorMessage = null; NotifyStateChanged(); try { var config = await _apiService.CreateConfigAsync(configType, variables, description); await ReloadConfigsInternalAsync(); SelectedConfig = config; NotifyStateChanged(); return config; } catch (Exception ex) { ErrorMessage = HttpErrorHelper.GetErrorMessage(ex); throw; } finally { IsSaving = false; NotifyStateChanged(); } } finally { _stateLock.Release(); } } /// /// Cập nhật config /// public async Task UpdateConfigAsync(List? variables = null, string? description = null) { if (SelectedConfig == null) { throw new InvalidOperationException("No config selected"); } await _stateLock.WaitAsync(); try { IsSaving = true; ErrorMessage = null; NotifyStateChanged(); try { SelectedConfig = await _apiService.UpdateConfigAsync(SelectedConfig.Id, variables, description); await ReloadConfigsInternalAsync(); NotifyStateChanged(); } catch (Exception ex) { ErrorMessage = HttpErrorHelper.GetErrorMessage(ex); throw; } finally { IsSaving = false; NotifyStateChanged(); } } finally { _stateLock.Release(); } } /// /// Xóa config /// public async Task DeleteConfigAsync(Guid id) { await _stateLock.WaitAsync(); try { IsSaving = true; ErrorMessage = null; NotifyStateChanged(); try { await _apiService.DeleteConfigAsync(id); await ReloadConfigsInternalAsync(); // Clear selection if deleted if (SelectedConfig?.Id == id) { SelectedConfig = null; NotifyStateChanged(); } } catch (Exception ex) { ErrorMessage = HttpErrorHelper.GetErrorMessage(ex); throw; } finally { IsSaving = false; NotifyStateChanged(); } } finally { _stateLock.Release(); } } /// /// Import config từ file /// public async Task ImportConfigAsync(Stream fileStream, string fileName, string configType, string? description = null) { await _stateLock.WaitAsync(); try { IsSaving = true; ErrorMessage = null; NotifyStateChanged(); try { var config = await _apiService.ImportConfigAsync(fileStream, fileName, configType, description); await ReloadConfigsInternalAsync(); SelectedConfig = config; NotifyStateChanged(); return config; } catch (Exception ex) { ErrorMessage = HttpErrorHelper.GetErrorMessage(ex); throw; } finally { IsSaving = false; NotifyStateChanged(); } } finally { _stateLock.Release(); } } /// /// Export config ra file /// public async Task ExportConfigAsync(Guid id) { try { return await _apiService.ExportConfigAsync(id); } catch (Exception ex) { ErrorMessage = HttpErrorHelper.GetErrorMessage(ex); throw; } } /// /// Cập nhật variable value /// public async Task UpdateVariableAsync(string variableName, object? value) { if (SelectedConfig == null) { throw new InvalidOperationException("No config selected"); } await _stateLock.WaitAsync(); try { IsSaving = true; ErrorMessage = null; NotifyStateChanged(); try { SelectedConfig = await _apiService.UpdateVariableAsync(SelectedConfig.Id, variableName, value); NotifyStateChanged(); } catch (Exception ex) { ErrorMessage = HttpErrorHelper.GetErrorMessage(ex); throw; } finally { IsSaving = false; NotifyStateChanged(); } } finally { _stateLock.Release(); } } /// /// Thêm variable /// public async Task AddVariableAsync(ConfigVariableModel variable) { if (SelectedConfig == null) { throw new InvalidOperationException("No config selected"); } await _stateLock.WaitAsync(); try { IsSaving = true; ErrorMessage = null; NotifyStateChanged(); try { SelectedConfig = await _apiService.AddVariableAsync(SelectedConfig.Id, variable); NotifyStateChanged(); } catch (Exception ex) { ErrorMessage = HttpErrorHelper.GetErrorMessage(ex); throw; } finally { IsSaving = false; NotifyStateChanged(); } } finally { _stateLock.Release(); } } /// /// Xóa variable /// public async Task RemoveVariableAsync(string variableName) { if (SelectedConfig == null) { throw new InvalidOperationException("No config selected"); } await _stateLock.WaitAsync(); try { IsSaving = true; ErrorMessage = null; NotifyStateChanged(); try { SelectedConfig = await _apiService.RemoveVariableAsync(SelectedConfig.Id, variableName); NotifyStateChanged(); } catch (Exception ex) { ErrorMessage = HttpErrorHelper.GetErrorMessage(ex); throw; } finally { IsSaving = false; NotifyStateChanged(); } } finally { _stateLock.Release(); } } /// /// Kiểm tra ConfigType có tồn tại không /// public async Task ConfigTypeExistsAsync(string configType) { try { return await _apiService.ConfigTypeExistsAsync(configType); } catch (HttpRequestException) { return false; } } // ========================================== // ROLE-BASED PERMISSION CHECKS // ========================================== /// /// Kiểm tra user hiện tại có quyền chỉnh sửa config không /// public async Task CanEditConfigAsync() { // Nếu EditorRole rỗng, mọi thứ hoạt động bình thường if (string.IsNullOrWhiteSpace(EditorRole)) { return true; } // Kiểm tra role của user hiện tại var userRoles = await GetCurrentUserRolesAsync(); return userRoles.Contains(EditorRole, StringComparer.OrdinalIgnoreCase); } /// /// Kiểm tra user hiện tại có quyền chỉnh sửa variable không /// public async Task CanEditVariableAsync(ConfigVariableModel variable) { // Nếu EditorRole rỗng, mọi thứ hoạt động bình thường if (string.IsNullOrWhiteSpace(EditorRole)) { return true; } var userRoles = await GetCurrentUserRolesAsync(); // Kiểm tra nếu user có role = EditorRole if (userRoles.Contains(EditorRole, StringComparer.OrdinalIgnoreCase)) { return true; } // Kiểm tra nếu role của user nằm trong Roles của variable if (!string.IsNullOrWhiteSpace(variable.Roles)) { var variableRoles = variable.Roles.Split(',', StringSplitOptions.RemoveEmptyEntries) .Select(r => r.Trim()) .Where(r => !string.IsNullOrWhiteSpace(r)); return variableRoles.Any(role => userRoles.Contains(role, StringComparer.OrdinalIgnoreCase)); } return false; } /// /// Lấy danh sách roles của user hiện tại /// private async Task> GetCurrentUserRolesAsync() { if (_authStateProvider == null) { return []; } try { var authState = await _authStateProvider.GetAuthenticationStateAsync(); var user = authState?.User; if (user == null || user.Identity?.IsAuthenticated != true) { return []; } // Lấy roles từ claims var roles = user.Claims .Where(c => c.Type == ClaimTypes.Role) .Select(c => c.Value) .ToList(); return roles; } catch (Exception ex) when (ex is InvalidOperationException or NullReferenceException) { return []; } } // ========================================== // PRIVATE METHODS // ========================================== /// /// Reload configs without acquiring the lock (for use inside locked methods) /// private async Task ReloadConfigsInternalAsync() { try { Configs = await _apiService.GetAllConfigsAsync(SearchQuery); } catch (Exception ex) { ErrorMessage = HttpErrorHelper.GetErrorMessage(ex); Configs = []; } } private void NotifyStateChanged() { OnStateChanged?.Invoke(); } }