using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using NexusShell.Models;
using NexusShell.Services;

namespace NexusShell.ViewModels;

public sealed class DeveloperConsoleViewModel : INotifyPropertyChanged
{
    private readonly AuthService _authService;
    private readonly DeveloperConsoleService _developerConsole;
    private readonly LockdownService _lockdown;
    private string _username = "admin@nexus.local";
    private string _password = string.Empty;
    private string? _errorMessage;
    private bool _isBusy;
    private bool _isAuthenticated;

    public DeveloperConsoleViewModel(
        AuthService authService,
        DeveloperConsoleService developerConsole,
        LockdownService lockdown)
    {
        _authService = authService;
        _developerConsole = developerConsole;
        _lockdown = lockdown;

        LoginCommand = new RelayCommand(async _ => await LoginAsync(), _ => !IsBusy && CanSubmit);
        ExitCommand = new RelayCommand(_ => ExitRequested?.Invoke(this, EventArgs.Empty));
        DisabledActionCommand = new RelayCommand(p => LogDisabledAction(p as string));

        RefreshAuditLog();
    }

    public event PropertyChangedEventHandler? PropertyChanged;
    public event EventHandler? ExitRequested;
    public event EventHandler? Authenticated;

    public string Username
    {
        get => _username;
        set { _username = value; OnPropertyChanged(); }
    }

    public string Password
    {
        get => _password;
        set { _password = value; OnPropertyChanged(); }
    }

    public string? ErrorMessage
    {
        get => _errorMessage;
        set { _errorMessage = value; OnPropertyChanged(); }
    }

    public bool IsBusy
    {
        get => _isBusy;
        set { _isBusy = value; OnPropertyChanged(); OnPropertyChanged(nameof(IsNotBusy)); }
    }

    public bool IsNotBusy => !IsBusy;

    public bool IsAuthenticated
    {
        get => _isAuthenticated;
        set { _isAuthenticated = value; OnPropertyChanged(); OnPropertyChanged(nameof(ShowLogin)); OnPropertyChanged(nameof(ShowConsole)); }
    }

    public bool ShowLogin => !IsAuthenticated;
    public bool ShowConsole => IsAuthenticated;

    public bool CanSubmit => Username.Contains('@') && Password.Length > 0;

    public ObservableCollection<LockdownAuditEntry> AuditEntries { get; } = [];

    public ICommand LoginCommand { get; }
    public ICommand ExitCommand { get; }
    public ICommand DisabledActionCommand { get; }

    public void Reset()
    {
        Password = string.Empty;
        ErrorMessage = null;
        IsAuthenticated = _developerConsole.IsAuthenticated;
        RefreshAuditLog();
    }

    private async Task LoginAsync()
    {
        ErrorMessage = null;

        if (_developerConsole.IsRateLimited)
        {
            ErrorMessage = _developerConsole.RateLimitMessage;
            return;
        }

        if (!_developerConsole.IsMasterUsername(Username))
        {
            _developerConsole.RecordFailedAttempt(Username);
            ErrorMessage = "Kun Master Developer har adgang.";
            RefreshAuditLog();
            return;
        }

        IsBusy = true;
        try
        {
            var (success, error) = await _authService.LoginAsync(Username, Password);
            if (!success)
            {
                _developerConsole.RecordFailedAttempt(Username);
                ErrorMessage = error ?? "Godkendelse fejlede";
                RefreshAuditLog();
                return;
            }

            _developerConsole.OpenSession(Username);
            IsAuthenticated = true;
            Password = string.Empty;
            RefreshAuditLog();
            Authenticated?.Invoke(this, EventArgs.Empty);
        }
        finally
        {
            IsBusy = false;
        }
    }

    private void LogDisabledAction(string? action)
    {
        if (string.IsNullOrWhiteSpace(action))
        {
            return;
        }

        _developerConsole.LogDisabledAction(action);
        RefreshAuditLog();
    }

    public void RefreshAuditLog()
    {
        AuditEntries.Clear();
        foreach (var entry in _lockdown.AuditLog.OrderByDescending(e => e.Timestamp).Take(20))
        {
            AuditEntries.Add(entry);
        }
    }

    public void CloseSession()
    {
        _developerConsole.CloseSession();
        IsAuthenticated = false;
        RefreshAuditLog();
    }

    private void OnPropertyChanged([CallerMemberName] string? name = null) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
