#nullable enable

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using Marechai.App.Services.Authentication;

namespace Marechai.App.Presentation.ViewModels;

/// <summary>
///     ViewModel for user management page (Uberadmin only)
/// </summary>
public partial class UsersViewModel : ObservableObject
{
    private readonly ApiClient               _apiClient;
    private readonly IJwtService             _jwtService;
    private readonly IStringLocalizer        _localizer;
    private readonly ILogger<UsersViewModel> _logger;
    private readonly ITokenService           _tokenService;

    [ObservableProperty]
    private ObservableCollection<string> _availableRoles = [];

    [ObservableProperty]
    private string _confirmPassword = string.Empty;

    [ObservableProperty]
    private string _dialogTitle = string.Empty;

    [ObservableProperty]
    private string _dialogType = string.Empty;

    private string? _editingUserId;

    [ObservableProperty]
    private string _email = string.Empty;

    [ObservableProperty]
    private string _errorMessage = string.Empty;

    [ObservableProperty]
    private bool _hasError;

    [ObservableProperty]
    private bool _isDataLoaded;

    [ObservableProperty]
    private bool _isLoading;

    [ObservableProperty]
    private string _password = string.Empty;

    [ObservableProperty]
    private string _phoneNumber = string.Empty;

    [ObservableProperty]
    private string? _selectedRole;

    [ObservableProperty]
    private UserDto? _selectedUser;

    [ObservableProperty]
    private string _userName = string.Empty;

    [ObservableProperty]
    private ObservableCollection<string> _userRoles = [];

    [ObservableProperty]
    private ObservableCollection<UserDto> _users = [];

    public UsersViewModel(ApiClient               apiClient, IJwtService      jwtService, ITokenService tokenService,
                          ILogger<UsersViewModel> logger,    IStringLocalizer localizer)
    {
        _apiClient    = apiClient;
        _jwtService   = jwtService;
        _tokenService = tokenService;
        _logger       = logger;
        _localizer    = localizer;

        LoadUsersCommand                = new AsyncRelayCommand(LoadUsersAsync);
        DeleteUserCommand               = new AsyncRelayCommand<UserDto>(DeleteUserAsync);
        OpenAddUserDialogCommand        = new AsyncRelayCommand(OpenAddUserDialogAsync);
        OpenEditUserDialogCommand       = new AsyncRelayCommand<UserDto>(OpenEditUserDialogAsync);
        OpenChangePasswordDialogCommand = new AsyncRelayCommand<UserDto>(OpenChangePasswordDialogAsync);
        OpenManageRolesDialogCommand    = new AsyncRelayCommand<UserDto>(OpenManageRolesDialogAsync);
        SaveUserCommand                 = new AsyncRelayCommand(SaveUserAsync);
        SavePasswordCommand             = new AsyncRelayCommand(SavePasswordAsync);
        AddRoleCommand                  = new AsyncRelayCommand(AddRoleAsync);
        RemoveRoleCommand               = new AsyncRelayCommand<string>(RemoveRoleAsync);
        CloseDialogCommand              = new RelayCommand(CloseDialog);
    }

    public IAsyncRelayCommand          LoadUsersCommand                { get; }
    public IAsyncRelayCommand<UserDto> DeleteUserCommand               { get; }
    public IAsyncRelayCommand          OpenAddUserDialogCommand        { get; }
    public IAsyncRelayCommand<UserDto> OpenEditUserDialogCommand       { get; }
    public IAsyncRelayCommand<UserDto> OpenChangePasswordDialogCommand { get; }
    public IAsyncRelayCommand<UserDto> OpenManageRolesDialogCommand    { get; }
    public IAsyncRelayCommand          SaveUserCommand                 { get; }
    public IAsyncRelayCommand          SavePasswordCommand             { get; }
    public IAsyncRelayCommand          AddRoleCommand                  { get; }
    public IAsyncRelayCommand<string>  RemoveRoleCommand               { get; }
    public IRelayCommand               CloseDialogCommand              { get; }

    /// <summary>
    ///     Checks if the current user is Uberadmin
    /// </summary>
    public bool IsUberadmin
    {
        get
        {
            string?              token = _tokenService.GetToken();
            IEnumerable<string>? roles = _jwtService.GetRoles(token);

            return roles.Contains("Uberadmin", StringComparer.OrdinalIgnoreCase);
        }
    }

    public event EventHandler<string>? ShowDialogRequested;

    private async Task LoadUsersAsync()
    {
        try
        {
            IsLoading    = true;
            HasError     = false;
            ErrorMessage = string.Empty;
            Users.Clear();

            List<UserDto>? usersResponse = await _apiClient.Users.GetAsync();

            if(usersResponse != null)
            {
                foreach(UserDto user in usersResponse) Users.Add(user);

                IsDataLoaded = true;
            }
        }
        catch(Exception ex)
        {
            _logger.LogError(ex, "Error loading users");
            ErrorMessage = _localizer["Failed to load users. Please try again."];
            HasError     = true;
        }
        finally
        {
            IsLoading = false;
        }
    }

    private async Task DeleteUserAsync(UserDto? user)
    {
        if(user?.Id == null) return;

        try
        {
            await _apiClient.Users[user.Id].DeleteAsync();
            await LoadUsersAsync();
        }
        catch(Exception ex)
        {
            _logger.LogError(ex, "Error deleting user");
            ErrorMessage = _localizer["Failed to delete user."];
            HasError     = true;
        }
    }

    private async Task OpenAddUserDialogAsync()
    {
        try
        {
            _editingUserId  = null;
            DialogTitle     = _localizer["Add User"];
            DialogType      = "AddEdit";
            Email           = string.Empty;
            UserName        = string.Empty;
            PhoneNumber     = string.Empty;
            Password        = string.Empty;
            ConfirmPassword = string.Empty;
            ShowDialogRequested?.Invoke(this, "AddEdit");
        }
        catch(Exception ex)
        {
            _logger.LogError(ex, "Error opening add user dialog");
        }
    }

    private async Task OpenEditUserDialogAsync(UserDto? user)
    {
        if(user?.Id == null) return;

        try
        {
            _editingUserId  = user.Id;
            DialogTitle     = _localizer["Edit User"];
            DialogType      = "AddEdit";
            Email           = user.Email       ?? string.Empty;
            UserName        = user.UserName    ?? string.Empty;
            PhoneNumber     = user.PhoneNumber ?? string.Empty;
            Password        = string.Empty;
            ConfirmPassword = string.Empty;
            ShowDialogRequested?.Invoke(this, "AddEdit");
        }
        catch(Exception ex)
        {
            _logger.LogError(ex, "Error opening edit user dialog");
        }
    }

    private async Task OpenChangePasswordDialogAsync(UserDto? user)
    {
        if(user?.Id == null) return;

        try
        {
            _editingUserId  = user.Id;
            DialogTitle     = _localizer["Change Password"];
            DialogType      = "Password";
            Password        = string.Empty;
            ConfirmPassword = string.Empty;
            ShowDialogRequested?.Invoke(this, "Password");
        }
        catch(Exception ex)
        {
            _logger.LogError(ex, "Error opening change password dialog");
        }
    }

    private async Task OpenManageRolesDialogAsync(UserDto? user)
    {
        if(user?.Id == null) return;

        try
        {
            _editingUserId = user.Id;
            DialogTitle    = _localizer["Manage Roles"];
            DialogType     = "Roles";

            // Load available roles
            List<string>? rolesResponse = await _apiClient.Users.Roles.GetAsync();
            AvailableRoles.Clear();

            if(rolesResponse != null)
            {
                foreach(string role in rolesResponse)
                    if(!string.IsNullOrWhiteSpace(role))
                        AvailableRoles.Add(role);
            }

            _logger.LogInformation($"Loaded {AvailableRoles.Count} available roles");

            // Load user's current roles
            UserRoles.Clear();

            if(user.Roles != null)
                foreach(string role in user.Roles)
                    UserRoles.Add(role);

            ShowDialogRequested?.Invoke(this, "Roles");
        }
        catch(Exception ex)
        {
            _logger.LogError(ex, "Error opening manage roles dialog");
        }
    }

    private async Task SaveUserAsync()
    {
        try
        {
            if(string.IsNullOrWhiteSpace(Email) || string.IsNullOrWhiteSpace(UserName))
            {
                ErrorMessage = _localizer["Email and username are required."];
                HasError     = true;

                return;
            }

            if(_editingUserId == null)
            {
                // Create new user
                if(string.IsNullOrWhiteSpace(Password))
                {
                    ErrorMessage = _localizer["Password is required for new users."];
                    HasError     = true;

                    return;
                }

                if(Password != ConfirmPassword)
                {
                    ErrorMessage = _localizer["Passwords do not match."];
                    HasError     = true;

                    return;
                }

                var createRequest = new CreateUserRequest
                {
                    Email       = Email,
                    UserName    = UserName,
                    PhoneNumber = PhoneNumber,
                    Password    = Password
                };

                await _apiClient.Users.PostAsync(createRequest);
            }
            else
            {
                // Update existing user
                var updateRequest = new UpdateUserRequest
                {
                    Email       = Email,
                    UserName    = UserName,
                    PhoneNumber = PhoneNumber
                };

                await _apiClient.Users[_editingUserId].PutAsync(updateRequest);
            }

            CloseDialog();
            await LoadUsersAsync();
        }
        catch(Exception ex)
        {
            _logger.LogError(ex, "Error saving user");
            ErrorMessage = _localizer["Failed to save user."];
            HasError     = true;
        }
    }

    private async Task SavePasswordAsync()
    {
        if(_editingUserId == null) return;

        try
        {
            if(string.IsNullOrWhiteSpace(Password))
            {
                ErrorMessage = _localizer["Password is required."];
                HasError     = true;

                return;
            }

            if(Password != ConfirmPassword)
            {
                ErrorMessage = _localizer["Passwords do not match."];
                HasError     = true;

                return;
            }

            var changePasswordRequest = new ChangePasswordRequest
            {
                NewPassword = Password
            };

            await _apiClient.Users[_editingUserId].Password.PostAsync(changePasswordRequest);

            CloseDialog();
        }
        catch(Exception ex)
        {
            _logger.LogError(ex, "Error changing password");
            ErrorMessage = _localizer["Failed to change password."];
            HasError     = true;
        }
    }

    private async Task AddRoleAsync()
    {
        if(_editingUserId == null || string.IsNullOrWhiteSpace(SelectedRole)) return;

        try
        {
            if(UserRoles.Contains(SelectedRole))
            {
                ErrorMessage = _localizer["User already has this role."];
                HasError     = true;

                return;
            }

            var addRoleRequest = new UserRoleRequest
            {
                RoleName = SelectedRole
            };

            await _apiClient.Users[_editingUserId].Roles.PostAsync(addRoleRequest);
            UserRoles.Add(SelectedRole);

            // Reload users to refresh the list
            await LoadUsersAsync();
        }
        catch(Exception ex)
        {
            _logger.LogError(ex, "Error adding role");
            ErrorMessage = _localizer["Failed to add role."];
            HasError     = true;
        }
    }

    private async Task RemoveRoleAsync(string? role)
    {
        if(_editingUserId == null || string.IsNullOrWhiteSpace(role)) return;

        try
        {
            await _apiClient.Users[_editingUserId].Roles[role].DeleteAsync();
            UserRoles.Remove(role);

            // Reload users to refresh the list
            await LoadUsersAsync();
        }
        catch(Exception ex)
        {
            _logger.LogError(ex, "Error removing role");
            ErrorMessage = _localizer["Failed to remove role."];
            HasError     = true;
        }
    }

    private void CloseDialog()
    {
        DialogType      = string.Empty;
        _editingUserId  = null;
        Email           = string.Empty;
        UserName        = string.Empty;
        PhoneNumber     = string.Empty;
        Password        = string.Empty;
        ConfirmPassword = string.Empty;
        UserRoles.Clear();
        AvailableRoles.Clear();
        HasError     = false;
        ErrorMessage = string.Empty;
    }
}