using AIStudio.Components;
using AIStudio.Tools.Rust;
using AIStudio.Tools.Services;

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;

namespace AIStudio.Dialogs;

/// <summary>
/// A dialog for capturing and configuring keyboard shortcuts.
/// </summary>
public partial class ShortcutDialog : MSGComponentBase
{
    [CascadingParameter]
    private IMudDialogInstance MudDialog { get; set; } = null!;

    [Inject]
    private RustService RustService { get; init; } = null!;

    /// <summary>
    /// The initial shortcut value (in internal format, e.g., "CmdOrControl+1").
    /// </summary>
    [Parameter]
    public string InitialShortcut { get; set; } = string.Empty;

    /// <summary>
    /// The identifier of the shortcut for conflict detection.
    /// </summary>
    [Parameter]
    public Shortcut ShortcutId { get; set; }
    
    private static readonly Dictionary<string, object?> USER_INPUT_ATTRIBUTES = new();

    private string currentShortcut = string.Empty;
    private string originalShortcut = string.Empty;
    private string currentDisplayName = string.Empty;
    private string validationMessage = string.Empty;
    private Severity validationSeverity = Severity.Info;
    private bool hasValidationError;

    //
    // Current key state:
    //
    private bool hasCtrl;
    private bool hasShift;
    private bool hasAlt;
    private bool hasMeta;
    private string? currentKey;
    private MudTextField<string>? inputField;

    #region Overrides of ComponentBase

    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();
        
        // Configure the spellchecking for the user input:
        this.SettingsManager.InjectSpellchecking(USER_INPUT_ATTRIBUTES);
        
        this.currentShortcut = this.InitialShortcut;
        this.originalShortcut = this.InitialShortcut;
        this.ParseExistingShortcut();
    }

    #endregion
    
    private string ShowText => string.IsNullOrWhiteSpace(this.currentShortcut)
        ? T("Press a key combination...")
        : this.GetDisplayShortcut();

    private void ParseExistingShortcut()
    {
        if (string.IsNullOrWhiteSpace(this.currentShortcut))
            return;

        // Parse the existing shortcut to set the state
        var parts = this.currentShortcut.Split('+');
        foreach (var part in parts)
        {
            switch (part.ToLowerInvariant())
            {
                case "cmdorcontrol":
                case "commandorcontrol":
                case "ctrl":
                case "control":
                case "cmd":
                case "command":
                    this.hasCtrl = true;
                    break;
                
                case "shift":
                    this.hasShift = true;
                    break;
                
                case "alt":
                    this.hasAlt = true;
                    break;
                
                case "meta":
                case "super":
                    this.hasMeta = true;
                    break;
                
                default:
                    this.currentKey = part;
                    break;
            }
        }
    }

    private async Task HandleKeyDown(KeyboardEventArgs e)
    {
        // Ignore pure modifier key presses:
        if (IsModifierKey(e.Code))
        {
            this.UpdateModifiers(e);
            this.currentKey = null;
            this.currentDisplayName = string.Empty;
            this.UpdateShortcutString();
            return;
        }

        this.UpdateModifiers(e);

        // Get the key:
        this.currentKey = TranslateKeyCode(e.Code);
        this.currentDisplayName = this.BuildDisplayShortcut(e.Key);

        // Validate: must have at least one modifier + a key
        if (!this.hasCtrl && !this.hasShift && !this.hasAlt && !this.hasMeta)
        {
            this.currentDisplayName = string.Empty;
            this.validationMessage = T("Please include at least one modifier key (Ctrl, Shift, Alt, or Cmd).");
            this.validationSeverity = Severity.Warning;
            this.hasValidationError = true;
            this.StateHasChanged();
            return;
        }

        this.UpdateShortcutString();
        await this.ValidateShortcut();
        
        this.StateHasChanged();
    }

    private void UpdateModifiers(KeyboardEventArgs e)
    {
        this.hasCtrl = e.CtrlKey || e.MetaKey; // Treat Meta (Cmd on Mac) same as Ctrl for cross-platform
        this.hasShift = e.ShiftKey;
        this.hasAlt = e.AltKey;
        this.hasMeta = e is { MetaKey: true, CtrlKey: false }; // Only set meta if not already using ctrl
    }

    private void UpdateShortcutString()
    {
        var parts = new List<string>();

        if (this.hasCtrl)
            parts.Add("CmdOrControl");
        
        if (this.hasShift)
            parts.Add("Shift");
        
        if (this.hasAlt)
            parts.Add("Alt");

        if (!string.IsNullOrWhiteSpace(this.currentKey))
            parts.Add(this.currentKey);

        this.currentShortcut = parts.Count > 0 ? string.Join("+", parts) : string.Empty;
        this.StateHasChanged();
    }

    private async Task ValidateShortcut()
    {
        if (string.IsNullOrWhiteSpace(this.currentShortcut) || string.IsNullOrWhiteSpace(this.currentKey))
        {
            this.validationMessage = string.Empty;
            this.hasValidationError = false;
            return;
        }

        // Check if the shortcut is valid by trying to register it with Rust
        var result = await this.RustService.ValidateShortcut(this.currentShortcut);
        if (result.IsValid)
        {
            if (!string.IsNullOrWhiteSpace(this.originalShortcut)
                && this.currentShortcut.Equals(this.originalShortcut, StringComparison.OrdinalIgnoreCase))
            {
                this.validationMessage = T("This is the shortcut you previously used.");
                this.validationSeverity = Severity.Info;
                this.hasValidationError = false;
                this.StateHasChanged();
                return;
            }
            
            if (result.HasConflict)
            {
                this.validationMessage = string.Format(T("This shortcut conflicts with: {0}"), result.ConflictDescription);
                this.validationSeverity = Severity.Warning;
                this.hasValidationError = false; // Allow saving, but warn
            }
            else
            {
                this.validationMessage = T("Shortcut is valid and available.");
                this.validationSeverity = Severity.Success;
                this.hasValidationError = false;
            }
        }
        else
        {
            this.validationMessage = string.Format(T("Invalid shortcut: {0}"), result.ErrorMessage);
            this.validationSeverity = Severity.Error;
            this.hasValidationError = true;
        }

        this.StateHasChanged();
    }

    private string GetDisplayShortcut()
    {
        if (!string.IsNullOrWhiteSpace(this.currentDisplayName))
            return this.currentDisplayName;

        // Convert internal format to display format:
        return this.currentShortcut
            .Replace("CmdOrControl", OperatingSystem.IsMacOS() ? "Cmd" : "Ctrl")
            .Replace("CommandOrControl", OperatingSystem.IsMacOS() ? "Cmd" : "Ctrl");
    }

    private void ClearShortcut()
    {
        this.currentShortcut = string.Empty;
        this.currentDisplayName = string.Empty;
        this.currentKey = null;
        this.hasCtrl = false;
        this.hasShift = false;
        this.hasAlt = false;
        this.hasMeta = false;
        this.validationMessage = string.Empty;
        this.hasValidationError = false;
        this.StateHasChanged();
    }

    private void Cancel() => this.MudDialog.Cancel();

    private void Confirm()
    {
        var displaySource = string.IsNullOrWhiteSpace(this.currentDisplayName)
            ? string.Empty
            : this.currentShortcut;

        this.MudDialog.Close(DialogResult.Ok(new ShortcutDialogResult(
            this.currentShortcut,
            this.currentDisplayName,
            displaySource)));
    }

    /// <summary>
    /// Checks if the key code represents a modifier key.
    /// </summary>
    private static bool IsModifierKey(string code) => code switch
    {
        "ShiftLeft" or "ShiftRight" => true,
        "ControlLeft" or "ControlRight" => true,
        "AltLeft" or "AltRight" => true,
        "MetaLeft" or "MetaRight" => true,
        
        _ => false,
    };

    /// <summary>
    /// Translates a JavaScript KeyboardEvent.code to Tauri shortcut format.
    /// </summary>
    private static string TranslateKeyCode(string code) => code switch
    {
        // Letters
        "KeyA" => "A",
        "KeyB" => "B",
        "KeyC" => "C",
        "KeyD" => "D",
        "KeyE" => "E",
        "KeyF" => "F",
        "KeyG" => "G",
        "KeyH" => "H",
        "KeyI" => "I",
        "KeyJ" => "J",
        "KeyK" => "K",
        "KeyL" => "L",
        "KeyM" => "M",
        "KeyN" => "N",
        "KeyO" => "O",
        "KeyP" => "P",
        "KeyQ" => "Q",
        "KeyR" => "R",
        "KeyS" => "S",
        "KeyT" => "T",
        "KeyU" => "U",
        "KeyV" => "V",
        "KeyW" => "W",
        "KeyX" => "X",
        "KeyY" => "Y",
        "KeyZ" => "Z",

        // Numbers
        "Digit0" => "0",
        "Digit1" => "1",
        "Digit2" => "2",
        "Digit3" => "3",
        "Digit4" => "4",
        "Digit5" => "5",
        "Digit6" => "6",
        "Digit7" => "7",
        "Digit8" => "8",
        "Digit9" => "9",

        // Function keys
        "F1" => "F1",
        "F2" => "F2",
        "F3" => "F3",
        "F4" => "F4",
        "F5" => "F5",
        "F6" => "F6",
        "F7" => "F7",
        "F8" => "F8",
        "F9" => "F9",
        "F10" => "F10",
        "F11" => "F11",
        "F12" => "F12",
        "F13" => "F13",
        "F14" => "F14",
        "F15" => "F15",
        "F16" => "F16",
        "F17" => "F17",
        "F18" => "F18",
        "F19" => "F19",
        "F20" => "F20",
        "F21" => "F21",
        "F22" => "F22",
        "F23" => "F23",
        "F24" => "F24",

        // Special keys
        "Space" => "Space",
        "Enter" => "Enter",
        "Tab" => "Tab",
        "Escape" => "Escape",
        "Backspace" => "Backspace",
        "Delete" => "Delete",
        "Insert" => "Insert",
        "Home" => "Home",
        "End" => "End",
        "PageUp" => "PageUp",
        "PageDown" => "PageDown",

        // Arrow keys
        "ArrowUp" => "Up",
        "ArrowDown" => "Down",
        "ArrowLeft" => "Left",
        "ArrowRight" => "Right",

        // Numpad
        "Numpad0" => "Num0",
        "Numpad1" => "Num1",
        "Numpad2" => "Num2",
        "Numpad3" => "Num3",
        "Numpad4" => "Num4",
        "Numpad5" => "Num5",
        "Numpad6" => "Num6",
        "Numpad7" => "Num7",
        "Numpad8" => "Num8",
        "Numpad9" => "Num9",
        "NumpadAdd" => "NumAdd",
        "NumpadSubtract" => "NumSubtract",
        "NumpadMultiply" => "NumMultiply",
        "NumpadDivide" => "NumDivide",
        "NumpadDecimal" => "NumDecimal",
        "NumpadEnter" => "NumEnter",

        // Punctuation
        "Minus" => "Minus",
        "Equal" => "Equal",
        "BracketLeft" => "BracketLeft",
        "BracketRight" => "BracketRight",
        "Backslash" => "Backslash",
        "Semicolon" => "Semicolon",
        "Quote" => "Quote",
        "Backquote" => "Backquote",
        "Comma" => "Comma",
        "Period" => "Period",
        "Slash" => "Slash",

        // Default: return as-is
        _ => code,
    };

    private string BuildDisplayShortcut(string? key)
    {
        var displayKey = GetDisplayKey(key);
        if (string.IsNullOrWhiteSpace(displayKey))
            return string.Empty;

        var parts = new List<string>();

        if (this.hasCtrl)
            parts.Add(OperatingSystem.IsMacOS() ? "Cmd" : "Ctrl");

        if (this.hasShift)
            parts.Add("Shift");

        if (this.hasAlt)
            parts.Add("Alt");

        parts.Add(displayKey);
        return string.Join("+", parts);
    }

    private static string GetDisplayKey(string? key) => key switch
    {
        null or "" => string.Empty,
        " " => "Space",
        "Control" or "Shift" or "Alt" or "Meta" => string.Empty,
        _ when key.Length == 1 && key[0] >= 'a' && key[0] <= 'z' => key.ToUpperInvariant(),
        _ => key,
    };

    private void HandleBlur()
    {
        // Re-focus the input field to keep capturing keys:
        this.inputField?.FocusAsync();
    }
}
