@using System.ComponentModel.DataAnnotations
@using Microsoft.Extensions.Localization
@using AliasVault.Client.Resources
@using AliasVault.Client.Main.Models
@inherits AliasVault.Client.Main.Pages.MainBase
@inject IJSRuntime JSRuntime
@inject ItemService ItemService
@inject AliasVault.Client.Services.QuickCreateStateService QuickCreateStateService
@inject LanguageService LanguageService
@implements IAsyncDisposable
@using AliasClientDb
@using AliasClientDb.Models
@using ItemTypeClass = AliasClientDb.Models.ItemType

<button @ref="buttonRef" @onclick="TogglePopup" id="quickIdentityButton" class="px-4 py-2 text-sm font-medium text-white bg-gradient-to-r from-primary-500 to-primary-600 hover:from-primary-600 hover:to-primary-700 focus:outline-none dark:from-primary-400 dark:to-primary-500 dark:hover:from-primary-500 dark:hover:to-primary-600 rounded-md shadow-sm transition duration-150 ease-in-out transform hover:scale-105 active:scale-95 focus:shadow-outline">
    @Localizer["NewAliasButtonShort"] <span class="hidden md:inline">@Localizer["NewAliasButtonText"].Value.Substring(1).Trim()</span>
</button>

@if (IsPopupVisible)
{
    <ClickOutsideHandler OnClose="ClosePopup" ContentId="quickIdentityPopup,quickIdentityButton">
        <div id="quickIdentityPopup" class="absolute z-50 mt-2 p-4 bg-white rounded-lg shadow-xl border border-gray-300 dark:bg-gray-800 dark:border-gray-400"
             style="@PopupStyle">
            @* Item Type Selector *@
            <div class="mb-4">
                <div class="flex gap-1">
                    @foreach (var itemType in ItemType.All)
                    {
                        <button type="button"
                                id="quickIdentityType_@itemType"
                                @onclick="() => SelectItemType(itemType)"
                                @onclick:preventDefault="true"
                                class="flex-1 px-2 py-2 text-xs font-medium rounded-md transition-colors flex flex-col items-center gap-1 @(Model.ItemType == itemType ? "bg-primary-100 text-primary-700 dark:bg-primary-900/30 dark:text-primary-300 border border-primary-300 dark:border-primary-700" : "bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600 border border-transparent")">
                            @GetTypeIcon(itemType)
                            <span>@GetTypeDisplayName(itemType)</span>
                        </button>
                    }
                </div>
            </div>

            <h3 class="text-lg font-semibold mb-4 text-gray-900 dark:text-white">@GetPopupTitle()</h3>
            <EditForm Model="Model" OnValidSubmit="HandleFormSubmit">
                <DataAnnotationsValidator />
                <div class="mb-4">
                    <EditFormRow Id="serviceName" Label="@Localizer["NameLabel"]" Placeholder="@GetNamePlaceholder()" @bind-Value="Model.ServiceName"></EditFormRow>
                    <ValidationMessage For="() => Model.ServiceName"/>
                </div>
                @if (Model.ItemType == ItemType.Login || Model.ItemType == ItemType.Alias)
                {
                    <div class="mb-4">
                        <EditFormRow Id="serviceUrl" Label="@Localizer["WebsiteUrlLabel"]" OnFocus="OnFocusUrlInput" @bind-Value="Model.ServiceUrl"></EditFormRow>
                        <ValidationMessage For="() => Model.ServiceUrl"/>
                    </div>
                }
                <div class="flex justify-between items-center">
                    <button id="quickIdentitySubmit" type="submit" class="@GetSubmitButtonClasses() text-white font-bold py-2 px-4 rounded flex items-center gap-2">
                        @if (Model.ItemType == ItemType.Alias)
                        {
                            @Localizer["CreateButton"]
                        }
                        else
                        {
                            @Localizer["ContinueButton"]
                            <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
                            </svg>
                        }
                    </button>
                </div>
            </EditForm>
        </div>
    </ClickOutsideHandler>
}

@code {
    private IStringLocalizer Localizer => LocalizerFactory.Create("Components.Main.Widgets.CreateNewIdentityWidget", "AliasVault.Client");

    private bool IsPopupVisible = false;
    private bool IsCreating = false;
    private CreateModel Model = new();
    private string PopupStyle { get; set; } = string.Empty;
    private ElementReference buttonRef;
    private IJSObjectReference? Module;

    /// <inheritdoc />
    async ValueTask IAsyncDisposable.DisposeAsync()
    {
        await KeyboardShortcutService.UnregisterShortcutAsync("gc");
        LanguageService.LanguageChanged -= OnLanguageChanged;
        if (Module is not null)
        {
            await Module.DisposeAsync();
        }
    }

    /// <inheritdoc />
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await KeyboardShortcutService.RegisterShortcutAsync("gc", ShowPopup);
            LanguageService.LanguageChanged += OnLanguageChanged;
            Module = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./js/modules/newIdentityWidget.js");
        }
    }

    /// <summary>
    /// Handles language change events and triggers component refresh.
    /// </summary>
    /// <param name="languageCode">The new language code.</param>
    private void OnLanguageChanged(string languageCode)
    {
        InvokeAsync(StateHasChanged);
    }

    /// <summary>
    /// When the URL input is focused, place cursor at the end of the default URL to allow for easy typing.
    /// </summary>
    private void OnFocusUrlInput(FocusEventArgs e)
    {
        if (Model.ServiceUrl != ItemService.DefaultServiceUrl)
        {
            return;
        }

        // Use a small delay to ensure the focus is set after the browser's default behavior.
        Task.Delay(1).ContinueWith(_ =>
        {
            JSRuntime.InvokeVoidAsync("eval", $"document.getElementById('serviceUrl').setSelectionRange({ItemService.DefaultServiceUrl.Length}, {ItemService.DefaultServiceUrl.Length})");
        });
    }

    /// <summary>
    /// Toggle the popup.
    /// </summary>
    private async Task TogglePopup()
    {
        IsPopupVisible = !IsPopupVisible;
        if (IsPopupVisible)
        {
            await ShowPopup();
        }
    }

    /// <summary>
    /// Show the popup.
    /// </summary>
    private async Task ShowPopup()
    {
        IsPopupVisible = true;

        // Clear the input fields and default to Login type
        Model = new();
        Model.ItemType = ItemType.Login;
        Model.ServiceUrl = ItemService.DefaultServiceUrl;

        await UpdatePopupStyle();
        await Task.Delay(100); // Give time for the DOM to update
        await JsInteropService.FocusElementById("serviceName");
    }

    /// <summary>
    /// Close the popup.
    /// </summary>
    private void ClosePopup()
    {
        IsPopupVisible = false;
    }

    /// <summary>
    /// Update the popup style so that it is positioned correctly.
    /// </summary>
    private async Task UpdatePopupStyle()
    {
        var windowWidth = await JSRuntime.InvokeAsync<int>("getWindowWidth");
        var buttonRect = await JSRuntime.InvokeAsync<BoundingClientRect>("getElementRect", buttonRef);

        // Constrain the popup width to 400px minus some padding.
        var popupWidth = Math.Min(400, windowWidth - 20);

        PopupStyle = $"width: {popupWidth}px; top: {buttonRect.Bottom}px;";
        StateHasChanged();
    }

    /// <summary>
    /// Select an item type.
    /// </summary>
    private async Task SelectItemType(string itemType)
    {
        Model.ItemType = itemType;
        StateHasChanged();

        // Refocus the service name input so user can continue typing
        await Task.Delay(50);
        await JsInteropService.FocusElementById("serviceName");
    }

    /// <summary>
    /// Handle form submission - either create alias directly or navigate to AddEdit page.
    /// </summary>
    private async Task HandleFormSubmit()
    {
        if (Model.ItemType == ItemType.Alias)
        {
            await CreateAlias();
        }
        else
        {
            NavigateToAddEdit();
        }
    }

    /// <summary>
    /// Create a new alias directly (quick create).
    /// </summary>
    private async Task CreateAlias()
    {
        if (IsCreating)
        {
            return;
        }

        IsCreating = true;
        GlobalLoadingSpinner.Show(Localizer["CreatingNewAliasMessage"]);
        StateHasChanged();

        var item = new Item
        {
            Name = Model.ServiceName,
            ItemType = ItemType.Alias,
            FieldValues = new List<FieldValue>()
        };

        // Set folder ID if we're in a folder view
        var folderId = GetCurrentFolderIdFromUrl();
        if (folderId.HasValue)
        {
            item.FolderId = folderId.Value;
        }

        // Set email with default domain
        ItemService.SetFieldValue(item, FieldKey.LoginEmail, "@" + ItemService.GetDefaultEmailDomain());

        // Set URL if provided
        if (Model.ServiceUrl != ItemService.DefaultServiceUrl)
        {
            ItemService.SetFieldValue(item, FieldKey.LoginUrl, Model.ServiceUrl);
        }

        // Generate random identity
        await ItemService.GenerateRandomIdentityAsync(item);

        var id = await ItemService.InsertEntryAsync(item);
        if (id == Guid.Empty)
        {
            // Error saving.
            IsCreating = false;
            GlobalLoadingSpinner.Hide();
            GlobalNotificationService.AddErrorMessage(Localizer["CreateItemErrorMessage"], true);
            return;
        }

        // No error, add success message.
        GlobalNotificationService.AddSuccessMessage(Localizer["ItemCreatedSuccessMessage"]);

        NavigationManager.NavigateTo("/items/" + id);

        IsCreating = false;
        GlobalLoadingSpinner.Hide();
        StateHasChanged();
        ClosePopup();
    }

    /// <summary>
    /// Navigate to the AddEdit page with prefilled data.
    /// </summary>
    private void NavigateToAddEdit()
    {
        // Store the form data in the state service to prefill in the AddEdit page.
        QuickCreateStateService.ServiceName = Model.ServiceName;
        QuickCreateStateService.ServiceUrl = Model.ServiceUrl;
        QuickCreateStateService.ItemType = Model.ItemType;
        QuickCreateStateService.FolderId = GetCurrentFolderIdFromUrl();

        NavigationManager.NavigateTo("/items/create");

        // Notify subscribers (e.g. AddEdit page) that the state has changed. This handles
        // the case where the user is already on the AddEdit page and the navigation above
        // does not trigger a re-initialization.
        QuickCreateStateService.NotifyStateChanged();

        ClosePopup();
    }

    /// <summary>
    /// Extract the folder ID from the current URL if we're in a folder view.
    /// </summary>
    /// <returns>The folder ID if in a folder view, null otherwise.</returns>
    private Guid? GetCurrentFolderIdFromUrl()
    {
        var uri = new Uri(NavigationManager.Uri);
        var path = uri.AbsolutePath;
        if (path.StartsWith("/items/folder/") && Guid.TryParse(path.Replace("/items/folder/", ""), out var folderId))
        {
            return folderId;
        }

        return null;
    }

    /// <summary>
    /// Get the popup title based on the selected item type.
    /// </summary>
    private string GetPopupTitle()
    {
        return Model.ItemType switch
        {
            ItemType.Alias => Localizer["CreateNewAliasTitle"],
            ItemType.Login => Localizer["CreateNewLoginTitle"],
            ItemType.CreditCard => Localizer["CreateNewCreditCardTitle"],
            ItemType.Note => Localizer["CreateNewNoteTitle"],
            _ => Localizer["CreateNewAliasTitle"]
        };
    }

    /// <summary>
    /// Get the name placeholder based on the selected item type.
    /// </summary>
    private string GetNamePlaceholder()
    {
        return Model.ItemType switch
        {
            ItemType.Login => Localizer["NamePlaceholderLogin"],
            ItemType.Alias => Localizer["NamePlaceholderAlias"],
            ItemType.CreditCard => Localizer["NamePlaceholderCard"],
            ItemType.Note => Localizer["NamePlaceholderNote"],
            _ => Localizer["NamePlaceholderLogin"]
        };
    }

    /// <summary>
    /// Get the submit button CSS classes based on the selected item type.
    /// </summary>
    private string GetSubmitButtonClasses()
    {
        return Model.ItemType == ItemType.Alias
            ? "bg-green-600 hover:bg-green-700"
            : "bg-primary-600 hover:bg-primary-700";
    }

    /// <summary>
    /// Get the display name for an item type.
    /// </summary>
    private string GetTypeDisplayName(string itemType)
    {
        return itemType switch
        {
            ItemType.Login => Localizer["TypeLogin"],
            ItemType.Alias => Localizer["TypeAlias"],
            ItemType.CreditCard => Localizer["TypeCard"],
            ItemType.Note => Localizer["TypeNote"],
            _ => itemType
        };
    }

    /// <summary>
    /// Get the icon markup for an item type.
    /// </summary>
    private static MarkupString GetTypeIcon(string itemType)
    {
        var svg = itemType switch
        {
            ItemType.Login => """<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z" /></svg>""",
            ItemType.Alias => """<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" /></svg>""",
            ItemType.CreditCard => """<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" /></svg>""",
            ItemType.Note => """<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" /></svg>""",
            _ => """<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" /></svg>"""
        };
        return new MarkupString(svg);
    }

    /// <summary>
    /// Bounding client rectangle returned from JavaScript.
    /// </summary>
    /// <remarks>
    /// Properties are populated via JavaScript interop deserialization.
    /// SonarCloud warnings are suppressed as these are required for JS interop.
    /// </remarks>
    private sealed class BoundingClientRect
    {
        public double Left { get; set; }
        public double Top { get; set; }
        public double Right { get; set; }
        public double Bottom { get; set; }
        public double Width { get; set; }
        public double Height { get; set; }
    }

    /// <summary>
    /// Local model for the form with support for validation.
    /// </summary>
    private sealed class CreateModel
    {
        /// <summary>
        /// The item type to create.
        /// </summary>
        public string ItemType { get; set; } = ItemTypeClass.Login;

        /// <summary>
        /// The service name.
        /// </summary>
        [Required(ErrorMessageResourceType = typeof(ValidationMessages), ErrorMessageResourceName = nameof(ValidationMessages.ServiceNameRequired))]
        [Display(Name = "Service Name")]
        public string ServiceName { get; set; } = string.Empty;

        /// <summary>
        /// The service URL.
        /// </summary>
        [Display(Name = "Service URL")]
        public string ServiceUrl { get; set; } = string.Empty;
    }
}
