@page "/login"
@using System.Diagnostics.CodeAnalysis
@using Iceshrimp.Frontend.Components
@using Iceshrimp.Frontend.Core.Miscellaneous
@using Iceshrimp.Frontend.Core.Schemas
@using Iceshrimp.Frontend.Core.Services
@using Iceshrimp.Frontend.Localization
@using Iceshrimp.Shared.Schemas.Web
@using Microsoft.Extensions.Localization
@using Iceshrimp.Assets.PhosphorIcons
@inject ApiService                     Api
@inject SessionService                 SessionService
@inject NavigationManager              Navigation
@inject IStringLocalizer<Localization> Loc;
@inject MetadataService                Metadata;
@inject IJSRuntime                     Js;
@inject ILogger<Login>                 Logger;
@layout UnauthLayout

<HeadTitle Text="@Loc["Login"]"/>

<div class="body">
    <img class="logo" src="/_content/Iceshrimp.Assets.Branding/splash.png"/>
    <span>
            <h3>@Loc["Login to {0}", Name ?? "this Iceshrimp.NET Instance."]</h3></span>
    <div class="login-form">
        <input placeholder="@Loc["Username"]" autocomplete="username" name="username" required="required"
               @bind="@Username"/>
        <input type="password" placeholder="@Loc["Password"]" autocomplete="current-password" name="current-password"
               required="required"
               @bind="@Password"/>
        <button class="button" @onclick="Submit" disabled="@Loading">@Loc["Login"]</button>
    </div>

    @if (Loading)
    {
        <span class="loading"><LoadingSpinner Scale="2"/></span>
    }
    @if (Failure)
    {
        <span>Authentication Failed</span>
    }
    @if (SessionService.Users.Count > 0)
    {
        <h2>@Loc["Existing sessions"]</h2>
        <div class="user-picker">
        @foreach(var user in SessionService.Users.Values)
        {
            <div class="user" tabindex="0" @onclick="() => LoginUser(user)">
                <UserAvatar User="@user" Size="4rem"/>
                <div class="username"><UserDisplayName User="user"></UserDisplayName></div>
            </div>
        }
        </div>
    }
    
    @if (Registration != Registrations.Closed)
    {
        <a class="register-link" href="/register">@Loc["Register a new account"]</a>
    }

    <dialog class="dialog" @ref="Dialog">
        <div class="dialog-content">
            <div class="header">
                <h2 class="title">@Loc["Two-Factor Code required"]</h2>
                <button class="button close" @onclick="() => CloseDialog(Dialog)">
                    <Icon Name="Icons.X"></Icon>
                </button>
            </div>
            <input class="otp-input" @bind="Otp" autocomplete="one-time-code" autofocus="autofocus"/>
            <StateButton ExtraClasses="button" @ref="OtpButton" OnClick="TwoFactorLogin">
                <Loading>@Loc["Processing"]</Loading>
                <Failed>@Loc["2FA Failed"]</Failed>
                <Success>@Loc["Success!"]</Success>
                <Initial>
                    @Loc["Login"]</Initial>
            </StateButton>
        </div>
        <div class="backdrop"></div>
    </dialog>

</div>

@code {
    [SupplyParameterFromQuery(Name = "rd")]
    [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")]
    private string? Redirect { get; set; }

    private          string?                        Password  { get; set; }
    private          string?                        Username  { get; set; }
    private          bool                           Loading   { get; set; }
    private          bool                           Failure   { get; set; }
    private          string?                        Name      { get; set; }
    private          ElementReference               Dialog    { get; set; }
    private          string?                        Otp       { get; set; }
    private          StateButton                    OtpButton { get; set; } = null!;
    private readonly Lazy<Task<IJSObjectReference>> _moduleTask;
    private          Registrations                  Registration { get; set; }

    public Login()
    {
        _moduleTask = new Lazy<Task<IJSObjectReference>>(() =>
                                                             Js.InvokeAsync<IJSObjectReference>(
                                                                                                "import",
                                                                                                "./Pages/Settings/Account.razor.js")
                                                               .AsTask());
    }

    private async Task Submit()
    {
        var module = await _moduleTask.Value;
        Loading = true;
        try
        {
            if (Username == null || Password == null)
            {
                Loading = false;
                Failure = true;
                StateHasChanged(); // Manually triggering a state update, else component will not re-render.
                return;
            }

            var res = await Api.Auth.LoginAsync(new AuthRequest { Username = Username, Password = Password });
            switch (res.Status)
            {
                case AuthStatusEnum.Authenticated:
                    SessionService.AddUser(new StoredUser
                    { // Token nor user will ever be null on an authenticated response
                        Id              = res.User!.Id,
                        Username        = res.User.Username,
                        DisplayName     = res.User.DisplayName,
                        AvatarUrl       = res.User.AvatarUrl,
                        BannerUrl       = res.User.BannerUrl,
                        InstanceName    = res.User.InstanceName,
                        InstanceIconUrl = res.User.InstanceIconUrl,
                        Token           = res.Token!,
                        Host            = res.User.Host,
                        IsAdmin         = res.IsAdmin ?? false,
                        IsModerator     = res.IsModerator ?? false,
                        Emojis          = res.User.Emojis,
                        MovedTo         = res.User.MovedTo
                    });
                    SessionService.SetSession(res.User.Id);
                    Navigation.NavigateTo(Uri.TryCreate(Redirect, UriKind.Relative, out _) ? Redirect : "/", true);
                    break;
                case AuthStatusEnum.Guest:
                    Failure = true;
                    Loading = false;
                    break;
                case AuthStatusEnum.TwoFactor:
                    await module.InvokeVoidAsync("openDialog", Dialog);
                    Api.SetBearerToken(res.Token!);
                    break;
            }
        }
        catch (ApiException)
        {
            Loading = false;
            Failure = false;
            StateHasChanged(); // Manually triggering a state update, else component will not re-render.
        }
    }

    private async Task TwoFactorLogin()
    {
        var module = await _moduleTask.Value;
        if (Otp is null)
        {
            return;
        }

        try
        {
            OtpButton.State = StateButton.StateEnum.Loading;
            StateHasChanged();
            var res = await Api.Auth.SubmitTwoFactorAsync(new TwoFactorRequest { Code = Otp });
            if (res.Status is AuthStatusEnum.Authenticated)
            {
                SessionService.AddUser(new StoredUser
                {
                    Id              = res.User!.Id,
                    Username        = res.User.Username,
                    DisplayName     = res.User.DisplayName,
                    AvatarUrl       = res.User.AvatarUrl,
                    BannerUrl       = res.User.BannerUrl,
                    InstanceName    = res.User.InstanceName,
                    InstanceIconUrl = res.User.InstanceIconUrl,
                    Token           = res.Token!,
                    Host            = res.User.Host,
                    IsAdmin         = res.IsAdmin ?? false,
                    IsModerator     = res.IsModerator ?? false,
                    Emojis          = res.User.Emojis,
                    MovedTo         = res.User.MovedTo
                });
                SessionService.SetSession(res.User.Id);
                Navigation.NavigateTo(Uri.TryCreate(Redirect, UriKind.Relative, out _) ? Redirect : "/", true);
            }
            else
            {
                Failure         = true;
                Loading         = false;
                OtpButton.State = StateButton.StateEnum.Failed;
                await module.InvokeVoidAsync("closeDialog", Dialog);
                OtpButton.State = StateButton.StateEnum.Initial;
            }
        }

        catch (ApiException e)
        {
            Logger.LogError(e, "2FA enrollment failed");
            OtpButton.State = StateButton.StateEnum.Failed;
        }
    }

    private async Task LoginUser(StoredUser user)
    {
        SessionService.SetSession(user.Id);
        await SessionService.UpdateCurrentUserAsync();
        Navigation.NavigateTo(Uri.TryCreate(Redirect, UriKind.Relative, out _) ? Redirect : "/", true);
    }

    protected override async Task OnInitializedAsync()
    {
        var metadata = await Metadata.Instance.Value;
        Name         = metadata.Name;
        Registration = metadata.Registration;
    }

    private async Task CloseDialog(ElementReference dialog)
    {
        var module = await _moduleTask.Value;
        await module.InvokeVoidAsync("closeDialog", dialog);
    }
}