using Insight.Infrastructure.Entities;
using Insight.Infrastructure.Services;
using Insight.Web.Constants;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using MudBlazor;

namespace Insight.Web.Pages.Account;

public partial class ProfileTwoFactorDialog
{
    [CascadingParameter(Name = "User")] public InsightUser? User { get; set; }

    [Parameter] public Func<Task>? OnToggle { get; set; }

    [Inject] private AuthenticatorService AuthenticatorService { get; init; } = default!;
    [Inject] private IJSRuntime JSRuntime { get; init; } = default!;
    [Inject] private ISnackbar Snackbar { get; init; } = default!;

    private enum Content { Options, Validate, Enable, Delete, UseRecovery, ResetRecovery, ShowRecovery }
    private Content _content = Content.Options;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await RefreshAsync().ConfigureAwait(false);
        }
    }

    private async Task RefreshAsync()
    {
        if (User is null) return;

        _authenticatorKey = await AuthenticatorService.GetKeyAsync(User).ConfigureAwait(false);
        if (string.IsNullOrEmpty(_authenticatorKey)) // if no key exists, generate one + recovery keys
        {
            await AuthenticatorService.ResetKeyAsync(User).ConfigureAwait(false);
            _authenticatorKey = await AuthenticatorService.GetKeyAsync(User).ConfigureAwait(false);

            var recoveryCodes = await AuthenticatorService.ResetRecoveryCodesAsync(User).ConfigureAwait(false);
            if (recoveryCodes is not null)
            {
                _recoveryCodes = new List<string>(recoveryCodes);
            }
        }

        if (_authenticatorKey is not null) _authenticatorFormatKey = AuthenticatorService.HumanizeKey(_authenticatorKey);

        _enabled = await AuthenticatorService.GetStatusAsync(User).ConfigureAwait(false);
        _recoveryCount = await AuthenticatorService.CountRecoveryCodesAsync(User).ConfigureAwait(false);

        await ClearQrCodeAsync("qrc").ConfigureAwait(false);
        await CreateQrCodeAsync("qrc", 270, 270).ConfigureAwait(false);
    }

    private async Task OnChangeAsync(Content content)
    {
        await RefreshAsync().ConfigureAwait(false);

        // if active site displays recovery codes, clear on switch
        if (_content == Content.ShowRecovery) _recoveryCodes = null;

        _content = content;

        await InvokeAsync(StateHasChanged).ConfigureAwait(false);
    }

    private async Task ValidateAsync(string? code)
    {
        if (User is null || string.IsNullOrWhiteSpace(code)) return;

        var validation = await AuthenticatorService.VerifyAsync(User, code).ConfigureAwait(false);
        if (validation is false)
        {
            Notification.Error(Snackbar, "Invalid 2FA Code");
            return;
        }

        Notification.Success(Snackbar, "Valid 2FA Code");

        await OnChangeAsync(Content.Options).ConfigureAwait(false);
    }

    private async Task EnableAsync(string? code)
    {
        Console.WriteLine(_code);

        if (User is null)
        {
            Notification.Error(Snackbar, "user null");
            return;
        }

        if (string.IsNullOrWhiteSpace(code))
        {
            Notification.Error(Snackbar, $"code null ({code})");
            return;
        }

        var validation = await AuthenticatorService.VerifyAsync(User, code).ConfigureAwait(false);
        if (validation is false)
        {
            Notification.Error(Snackbar, "Invalid 2FA Code");
            return;
        }

        var result = await AuthenticatorService.EnableAsync(User).ConfigureAwait(false);
        if (result is false)
        {
            Notification.Error(Snackbar, "Error");
            return;
        }

        Notification.Success(Snackbar, "Enabled 2FA");

        await OnChangeAsync(Content.Options).ConfigureAwait(false);
    }

    private async Task DisableAsync()
    {
        if (User is null) return;

        var result = await AuthenticatorService.DisableAsync(User).ConfigureAwait(false);
        if (result is false)
        {
            Notification.Error(Snackbar, "Error");
            return;
        }

        Notification.Success(Snackbar, "Disabled 2FA");

        await OnChangeAsync(Content.Options).ConfigureAwait(false);
    }

    private async Task DeleteAsync()
    {
        if (User is null) return;

        var result = await AuthenticatorService.DeleteAsync(User).ConfigureAwait(false);
        if (result is false)
        {
            Notification.Error(Snackbar, "Error");
            return;
        }

        Notification.Success(Snackbar, "Deleted 2FA");
        await OnChangeAsync(Content.Options).ConfigureAwait(false);
    }

    private async Task UseRecoveryAsync(string? code)
    {
        if (User is null || string.IsNullOrWhiteSpace(code)) return;

        await CountRecoveryAsync().ConfigureAwait(false);

        if (_recoveryCount == 0)
        {
            Notification.Error(Snackbar, "No Recovery Codes Left");
            return;
        }

        var result = await AuthenticatorService.UseRecoveryCodeAsync(User, code).ConfigureAwait(false);
        if (result is false)
        {
            Notification.Error(Snackbar, "Invalid Code");
            return;
        }

        await CountRecoveryAsync().ConfigureAwait(false);

        Notification.Success(Snackbar, $"Recovery Codes Left: ({_recoveryCount})");
        await OnChangeAsync(Content.Options).ConfigureAwait(false);
    }

    private async Task ResetRecoveryAsync()
    {
        if (User is null) return;

        var recoveryCodes = await AuthenticatorService.ResetRecoveryCodesAsync(User).ConfigureAwait(false);
        if (recoveryCodes is null)
        {
            Notification.Error(Snackbar, "Error");
            return;
        }

        if (recoveryCodes is not null)
        {
            _recoveryCodes = new List<string>(recoveryCodes);
        }

        Notification.Success(Snackbar, "Reset 2FA Recovery Codes");
        await OnChangeAsync(Content.ShowRecovery).ConfigureAwait(false);
    }

    private async Task CountRecoveryAsync()
    {
        if (User is null) return;

        _recoveryCount = await AuthenticatorService.CountRecoveryCodesAsync(User).ConfigureAwait(false);
    }

    private async Task CreateQrCodeAsync(string elementId, int width, int height)
    {
        if (User is null || User.Email is null) return;
        if (_authenticatorKey is null) return;

        var code = AuthenticatorService.GenerateQrCode(User.Email, _authenticatorKey);

        await JSRuntime.InvokeVoidAsync("createQrCode", elementId, code, width, height).ConfigureAwait(false);
    }

    private async Task ClearQrCodeAsync(string elementId)
    {
        await JSRuntime.InvokeVoidAsync("clearQrCode", elementId).ConfigureAwait(false);
    }
}