@page "/settings/import-export"
@inherits MainBase
@inject ItemService ItemService
@inject ILogger<ImportExport> Logger
@inject ConfirmModalService ConfirmModalService
@inject AliasVault.Client.Services.Crypto.AvexCryptoService AvexCrypto
@using Microsoft.Extensions.Localization
@using AliasVault.RazorComponents.Services
@using AliasVault.Client.Main.Pages.Settings.ImportExport.Components
@using AliasVault.Client.Main.Components.Shared
@using AliasVault.Client.Services.Auth
@using AliasVault.Client.Services.Auth.Enums
@using AliasVault.ImportExport
@using Microsoft.EntityFrameworkCore
@using AliasClientDb

<LayoutPageTitle>@Localizer["PageTitle"]</LayoutPageTitle>

<PageHeader
    BreadcrumbItems="@BreadcrumbItems"
    Title="@Localizer["PageTitle"]"
    Description="@Localizer["PageDescription"]">
</PageHeader>

<PageContent>
<div class="p-4 mx-4 mb-4 bg-white border border-gray-200 rounded-lg shadow-sm 2xl:col-span-2 dark:border-gray-700 sm:p-6 dark:bg-gray-800">
    <h3 class="mb-4 text-xl font-semibold dark:text-white">@Localizer["ImportSectionTitle"]</h3>
    <div class="mb-4 text-sm text-gray-500 dark:text-gray-400">
        @((MarkupString)Localizer["ImportSectionDescription"].Value)
    </div>
    <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
        <ImportServiceAliasVault />
        <ImportService1Password />
        <ImportServiceBitwarden />
        <ImportServiceChrome />
        <ImportServiceDashlane />
        <ImportServiceDropbox />
        <ImportServiceEdge />
        <ImportServiceEnpass />
        <ImportServiceFirefox />
        <ImportServiceKeePass />
        <ImportServiceKeePassXC />
        <ImportServiceLastPass />
        <ImportServiceNordPass />
        <ImportServiceProtonPass />
        <ImportServiceRoboform />
        <ImportServiceStrongbox />
        <ImportServiceGenericCsv />
    </div>
</div>

<div class="p-4 mx-4 mb-4 bg-white border border-gray-200 rounded-lg shadow-sm 2xl:col-span-2 dark:border-gray-700 sm:p-6 dark:bg-gray-800">
    <h3 class="mb-4 text-xl font-semibold dark:text-white">@Localizer["ExportSectionTitle"]</h3>
    <p class="text-sm text-gray-500 dark:text-gray-400 mb-4">
        @Localizer["ExportSectionDescription"]
    </p>

    <div class="space-y-3">
        <!-- Encrypted Export (.avex) - Recommended -->
        <div class="flex items-center justify-between p-3 border-2 border-orange-200 dark:border-orange-800 rounded-lg bg-orange-50 dark:bg-orange-900/20">
            <div class="flex-1 min-w-0 mr-4">
                <div class="flex items-center gap-2 mb-1">
                    <h4 class="text-sm font-semibold text-gray-900 dark:text-white">@Localizer["ExportAvexTitle"]</h4>
                    <span class="px-1.5 py-0.5 text-xs font-medium text-orange-700 dark:text-orange-300 bg-orange-100 dark:bg-orange-900 rounded">@Localizer["RecommendedLabel"]</span>
                </div>
                <p class="text-xs text-gray-600 dark:text-gray-300">
                    @Localizer["ExportAvexDescription"]
                </p>
            </div>
            <Button OnClick="@(() => ShowExportEncryptedConfirmation())">@Localizer["ExportAvexButton"]</Button>
        </div>

        <!-- Unencrypted Export (.avux) -->
        <div class="flex items-center justify-between p-3 border border-gray-200 dark:border-gray-700 rounded-lg">
            <div class="flex-1 min-w-0 mr-4">
                <h4 class="text-sm font-semibold text-gray-900 dark:text-white mb-1">@Localizer["ExportAvuxTitle"]</h4>
                <p class="text-xs text-gray-600 dark:text-gray-300">
                    @Localizer["ExportAvuxDescription"]
                </p>
            </div>
            <Button OnClick="@(() => ShowExportConfirmation(ExportType.Avux))">@Localizer["ExportAvuxButton"]</Button>
        </div>

        <!-- CSV Export -->
        <div class="flex items-center justify-between p-3 border border-gray-200 dark:border-gray-700 rounded-lg">
            <div class="flex-1 min-w-0 mr-4">
                <h4 class="text-sm font-semibold text-gray-900 dark:text-white mb-1">@Localizer["ExportCsvTitle"]</h4>
                <p class="text-xs text-gray-600 dark:text-gray-300">
                    @Localizer["ExportCsvDescription"]
                </p>
            </div>
            <Button OnClick="@(() => ShowExportConfirmation(ExportType.Csv))">@Localizer["ExportCsvButton"]</Button>
        </div>

@* DEBUG-only raw SQLite export. *@
@if (IsDebugBuild)
{
    <div class="flex items-center justify-between p-3 border-2 border-dashed border-yellow-400 dark:border-yellow-600 rounded-lg bg-yellow-50 dark:bg-yellow-900/20">
        <div class="flex-1 min-w-0 mr-4">
            <div class="flex items-center gap-2 mb-1">
                <h4 class="text-sm font-semibold text-gray-900 dark:text-white">Export raw SQLite</h4>
                <span class="px-1.5 py-0.5 text-xs font-medium text-yellow-800 dark:text-yellow-200 bg-yellow-200 dark:bg-yellow-800 rounded">DEBUG</span>
            </div>
            <p class="text-xs text-gray-600 dark:text-gray-300">
                Downloads the unencrypted SQLite vault file as-is. Anyone with the file can read everything.
            </p>
        </div>
        <Button OnClick="@ExportVaultSqlite">Download .sqlite</Button>
    </div>
}
    </div>
</div>

<ResetVaultSection />
</PageContent>

<PasswordConfirmationModal
    IsOpen="@_showPasswordConfirmation"
    Title="@Localizer["ExportPasswordConfirmTitle"]"
    Description="@Localizer["ExportPasswordConfirmDescription"]"
    ErrorMessage="@_passwordError"
    OnPasswordSubmitted="@HandlePasswordSubmitted"
    OnClose="@ClosePasswordConfirmation" />

<ExportPasswordModal
    IsOpen="@_showExportPasswordModal"
    Title="@Localizer["ExportEncryptedPasswordTitle"]"
    Description="@Localizer["ExportEncryptedPasswordDescription"]"
    ErrorMessage="@_exportPasswordError"
    OnPasswordSubmitted="@HandleExportPasswordSubmitted"
    OnClose="@CloseExportPasswordModal" />

@code {
    // Assign debug static flag for Razor markup check.
#if DEBUG
    private static readonly bool IsDebugBuild = true;
#else
    private static readonly bool IsDebugBuild = false;
#endif

    private string _username = string.Empty;
    private bool _showPasswordConfirmation;
    private string _passwordError = string.Empty;
    private bool _showExportPasswordModal;
    private string _exportPasswordError = string.Empty;
    private IStringLocalizer Localizer => LocalizerFactory.Create("Pages.Main.Settings.ImportExport.ImportExport", "AliasVault.Client");
    private ExportType _currentExportType;

    private enum ExportType
    {
        Csv,
        Avux,
        Avex
    }

    /// <inheritdoc />
    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();
        BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = Localizer["PageTitle"] });

        // Get username for password verification
        _username = await GetUsernameAsync();
    }

    private async Task ShowExportConfirmation(ExportType exportType)
    {
        _currentExportType = exportType;
        var confirmMessage = Localizer["ExportWarningMessage"];

        var result = await ConfirmModalService.ShowConfirmation(Localizer["ExportConfirmTitle"], confirmMessage, SharedLocalizer["Confirm"], SharedLocalizer["Cancel"]);
        if (!result)
        {
            return;
        }

        // Show password confirmation modal
        _passwordError = string.Empty;
        _showPasswordConfirmation = true;
        StateHasChanged();
    }

    private async Task ShowExportEncryptedConfirmation()
    {
        _currentExportType = ExportType.Avex;
        var confirmMessage = Localizer["ExportEncryptedWarningMessage"];

        var result = await ConfirmModalService.ShowConfirmation(Localizer["ExportConfirmTitle"], confirmMessage, SharedLocalizer["Confirm"], SharedLocalizer["Cancel"]);
        if (!result)
        {
            return;
        }

        // For all export methods, require master password confirmation for security
        _passwordError = string.Empty;
        _showPasswordConfirmation = true;
        StateHasChanged();
    }

    /// <summary>
    /// Handles the password submission - verifies password and proceeds with export.
    /// </summary>
    private async Task HandlePasswordSubmitted(string password)
    {
        // Close modal first and show global loading spinner
        _showPasswordConfirmation = false;
        _passwordError = string.Empty;
        StateHasChanged();

        GlobalLoadingSpinner.Show(Localizer["VerifyingPasswordMessage"]);

        try
        {
            var result = await AuthService.VerifyPasswordAsync(_username, password);

            switch (result)
            {
                case PasswordVerificationResult.Success:
                    GlobalLoadingSpinner.Hide();
                    // For encrypted export, show export password modal after master password verification
                    if (_currentExportType == ExportType.Avex)
                    {
                        _exportPasswordError = string.Empty;
                        _showExportPasswordModal = true;
                        StateHasChanged();
                    }
                    else
                    {
                        await HandleExportConfirmed();
                    }
                    break;

                case PasswordVerificationResult.InvalidPassword:
                    GlobalLoadingSpinner.Hide();
                    _passwordError = Localizer["PasswordIncorrect"];
                    _showPasswordConfirmation = true;
                    StateHasChanged();
                    break;

                case PasswordVerificationResult.ServerError:
                default:
                    GlobalLoadingSpinner.Hide();
                    _passwordError = Localizer["PasswordVerificationFailed"];
                    _showPasswordConfirmation = true;
                    StateHasChanged();
                    break;
            }
        }
        catch (Exception ex)
        {
            Logger.LogError(ex, "Error verifying password");
            GlobalLoadingSpinner.Hide();
            _passwordError = Localizer["PasswordVerificationFailed"];
            _showPasswordConfirmation = true;
            StateHasChanged();
        }
    }

    /// <summary>
    /// Closes the password confirmation modal.
    /// </summary>
    private void ClosePasswordConfirmation()
    {
        _showPasswordConfirmation = false;
        _passwordError = string.Empty;
        StateHasChanged();
    }

    private async Task HandleExportPasswordSubmitted(string exportPassword)
    {
        // Close modal first
        _showExportPasswordModal = false;
        _exportPasswordError = string.Empty;
        StateHasChanged();

        await ExportVaultAvex(exportPassword);
    }

    private void CloseExportPasswordModal()
    {
        _showExportPasswordModal = false;
        _exportPasswordError = string.Empty;
        StateHasChanged();
    }

    private async Task HandleExportConfirmed()
    {
        switch (_currentExportType)
        {
            case ExportType.Csv:
                await ExportVaultCsv();
                break;
            case ExportType.Avux:
                await ExportVaultAvux();
                break;
            case ExportType.Avex:
                break;
        }
    }

    private async Task ExportVaultCsv()
    {
        try
        {
            var items = await ItemService.LoadAllAsync();

            var csvBytes = ItemCsvService.ExportItemsToCsv(items);

            // Create a memory stream from the byte array.
            using (MemoryStream memoryStream = new MemoryStream(csvBytes))
            {
                // Invoke JavaScript to initiate the download with date and username in filename
                await JsInteropService.DownloadFileFromStream($"{await GetExportFileName("csv")}", memoryStream.ToArray());
            }

            GlobalNotificationService.AddSuccessMessage(Localizer["ExportSuccessMessage"], true);
        }
        catch (Exception ex)
        {
            Logger.LogError(ex, "Error downloading file");
        }
    }

    // Always defined so the markup binding compiles, only invoked when IsDebugBuild is true.
    private async Task ExportVaultSqlite()
    {
        GlobalLoadingSpinner.Show("Exporting SQLite vault...");
        try
        {
            var base64 = await DbService.ExportSqliteToBase64Async();
            var bytes = Convert.FromBase64String(base64);
            await JsInteropService.DownloadFileFromStream(await GetExportFileName("sqlite"), bytes);
        }
        catch (Exception ex)
        {
            Logger.LogError(ex, "Error exporting raw SQLite vault");
            GlobalNotificationService.AddErrorMessage(SharedLocalizer["ErrorGeneric"], true);
        }
        finally
        {
            GlobalLoadingSpinner.Hide();
        }
    }

    private async Task ExportVaultAvux()
    {
        GlobalLoadingSpinner.Show(Localizer["ExportingVaultMessage"]);
        try
        {
            var avuxBytes = await GenerateAvuxBytesAsync();

            await JsInteropService.DownloadFileFromStream(
                $"{await GetExportFileName("avux")}",
                avuxBytes);

            GlobalNotificationService.AddSuccessMessage(Localizer["ExportSuccessMessage"], true);
        }
        catch (Exception ex)
        {
            Logger.LogError(ex, "Error exporting vault to .avux format");
            GlobalNotificationService.AddErrorMessage(
                SharedLocalizer["ErrorGeneric"],
                true);
        }
        finally
        {
            GlobalLoadingSpinner.Hide();
        }
    }

    /// <summary>
    /// Generates .avux bytes from the current vault data.
    /// This is used by both .avux export (unencrypted) and .avex export (encrypted).
    /// </summary>
    private async Task<byte[]> GenerateAvuxBytesAsync()
    {
        var context = await DbService.GetDbContextAsync();

        // Load all vault entities with their related data
        var items = await context.Items
            .Where(i => !i.IsDeleted && i.DeletedAt == null)
            .Include(i => i.FieldValues)
            .Include(i => i.Attachments)
            .Include(i => i.TotpCodes)
            .Include(i => i.Passkeys)
            .ToListAsync();

        var folders = await context.Folders
            .Where(f => !f.IsDeleted)
            .ToListAsync();

        var tags = await context.Tags
            .Where(t => !t.IsDeleted)
            .ToListAsync();

        var itemTags = await context.ItemTags
            .Where(it => !it.IsDeleted)
            .ToListAsync();

        var fieldDefinitions = await context.FieldDefinitions
            .Where(fd => !fd.IsDeleted)
            .ToListAsync();

        // Get unique logo IDs from items
        var logoIds = items
            .Where(i => i.LogoId.HasValue)
            .Select(i => i.LogoId!.Value)
            .Distinct()
            .ToList();

        // Load logos used by items
        var logos = await context.Logos
            .Where(l => !l.IsDeleted && logoIds.Contains(l.Id))
            .ToListAsync();

        var username = await GetUsernameAsync();

        // Export to .avux format
        return await VaultExportService.ExportToAvuxAsync(
            items,
            folders,
            tags,
            itemTags,
            fieldDefinitions,
            logos,
            username);
    }

    private async Task ExportVaultAvex(string exportPassword)
    {
        GlobalLoadingSpinner.Show(Localizer["ExportingVaultMessage"]);
        try
        {
            var avuxBytes = await GenerateAvuxBytesAsync();

            var username = await GetUsernameAsync();
            var avexBytes = await AvexCrypto.EncryptToAvexAsync(
                avuxBytes,
                exportPassword,
                username);

            await JsInteropService.DownloadFileFromStream(
                $"{await GetExportFileName("avex")}",
                avexBytes);

            GlobalNotificationService.AddSuccessMessage(Localizer["ExportSuccessMessage"], true);
        }
        catch (Exception ex)
        {
            Logger.LogError(ex, "Error exporting vault to .avex format");
            GlobalNotificationService.AddErrorMessage(
                SharedLocalizer["ErrorGeneric"],
                true);
        }
        finally
        {
            GlobalLoadingSpinner.Hide();
        }
    }

    /// <summary>
    /// Creates a filename for the export file based on the current date and username.
    /// </summary>
    /// <param name="extension">The extension of the file.</param>
    /// <returns>The export file name.</returns>
    private async Task<string> GetExportFileName(string extension)
    {
        var dateStr = DateTime.UtcNow.ToString("yyyy-MM-dd");
        var username = await GetUsernameAsync();
        return $"aliasvault-export-{username}-{dateStr}.{extension}";
    }
}
