@page "/Account/Register"

@using System.ComponentModel.DataAnnotations
@using System.Text
@using System.Text.Encodings.Web
@using Microsoft.AspNetCore.Identity
@using Microsoft.AspNetCore.WebUtilities
@using Remotely.Server.Data
@using Remotely.Shared

@inject UserManager<RemotelyUser> UserManager
@inject IUserStore<RemotelyUser> UserStore
@inject SignInManager<RemotelyUser> SignInManager
@inject IEmailSender<RemotelyUser> EmailSender
@inject ILogger<Register> Logger
@inject NavigationManager NavigationManager
@inject IdentityRedirectManager RedirectManager
@inject IDataService DataService
@inject IWebHostEnvironment HostEnv

<PageTitle>Register</PageTitle>
<h1>Register</h1>

@if (!_registrationEnabled)
{
    <h2>Registration is disabled.</h2>
}
else
{

    <div class="row">
        <div class="col-md-4">
            <StatusMessage Message="@Message" />
            <EditForm Model="Input" asp-route-returnUrl="@ReturnUrl" method="post" OnValidSubmit="RegisterUser" FormName="register">
                <DataAnnotationsValidator />
                <h2>Create a new account.</h2>
                <hr />
                <ValidationSummary class="text-danger" role="alert" />
                <div class="form-floating mb-3">
                    <InputText @bind-Value="Input.Email" class="form-control" autocomplete="username" aria-required="true" placeholder="name@example.com" />
                    <label for="email">Email</label>
                    <ValidationMessage For="() => Input.Email" class="text-danger" />
                </div>
                <div class="form-floating mb-3">
                    <InputText type="password" @bind-Value="Input.Password" class="form-control" autocomplete="new-password" aria-required="true" placeholder="password" />
                    <label for="password">Password</label>
                    <ValidationMessage For="() => Input.Password" class="text-danger" />
                </div>
                <div class="form-floating mb-3">
                    <InputText type="password" @bind-Value="Input.ConfirmPassword" class="form-control" autocomplete="new-password" aria-required="true" placeholder="password" />
                    <label for="confirm-password">Confirm Password</label>
                    <ValidationMessage For="() => Input.ConfirmPassword" class="text-danger" />
                </div>
                <button type="submit" class="w-100 btn btn-lg btn-primary">Register</button>
            </EditForm>
        </div>
        @*<div class="col-md-6 col-md-offset-2">
            <section>
                <h3>Use another service to register.</h3>
                <hr />
                <ExternalLoginPicker />
            </section>
        </div> *@
    </div>
}


@code {
    private IEnumerable<IdentityError>? identityErrors;
    private bool _registrationEnabled;

    [SupplyParameterFromForm]
    private InputModel Input { get; set; } = new();

    [SupplyParameterFromQuery]
    private string? ReturnUrl { get; set; }

    private string? Message => identityErrors is null ? null : $"Error: {string.Join(", ", identityErrors.Select(error => error.Description))}";

    protected override async Task OnInitializedAsync()
    {
        _registrationEnabled = await IsRegistrationEnabled();
        await base.OnInitializedAsync();
    }

    public async Task RegisterUser(EditContext editContext)
    {
        if (!await IsRegistrationEnabled())
        {
            return;
        }

        var user = CreateUser();

        await UserStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
        var emailStore = GetEmailStore();
        await emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
        var result = await UserManager.CreateAsync(user, Input.Password);

        if (!result.Succeeded)
        {
            identityErrors = result.Errors;
            return;
        }

        Logger.LogInformation("User created a new account with password.");

        var userId = await UserManager.GetUserIdAsync(user);
        var code = await UserManager.GenerateEmailConfirmationTokenAsync(user);
        code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
        var callbackUrl = NavigationManager.GetUriWithQueryParameters(
            NavigationManager.ToAbsoluteUri("Account/ConfirmEmail").AbsoluteUri,
            new Dictionary<string, object?> { ["userId"] = userId, ["code"] = code, ["returnUrl"] = ReturnUrl });

        await EmailSender.SendConfirmationLinkAsync(user, Input.Email, HtmlEncoder.Default.Encode(callbackUrl));

        if (UserManager.Options.SignIn.RequireConfirmedAccount)
        {
            RedirectManager.RedirectTo(
                "Account/RegisterConfirmation",
                new() { ["email"] = Input.Email, ["returnUrl"] = ReturnUrl });
        }

        await SignInManager.SignInAsync(user, isPersistent: false);
        RedirectManager.RedirectTo(ReturnUrl);
    }

    private RemotelyUser CreateUser()
    {
        var organizationCount = DataService.GetOrganizationCount();
        var user = new RemotelyUser
            {
                UserName = Input.Email,
                Email = Input.Email,
                IsServerAdmin = organizationCount == 0,
                Organization = new Organization()
                {
                    OrganizationName = string.Empty,
                    IsDefaultOrganization = organizationCount == 0
                },
                UserOptions = new RemotelyUserOptions(),
                IsAdministrator = true,
                LockoutEnabled = true
            };

        if (organizationCount == 0 && HostEnv.IsDevelopment())
        {
            user.Organization.ID = AppConstants.DebugOrgId;
            user.OrganizationID = AppConstants.DebugOrgId;
        }
        return user;
    }

    private IUserEmailStore<RemotelyUser> GetEmailStore()
    {
        if (!UserManager.SupportsUserEmail)
        {
            throw new NotSupportedException("The default UI requires a user store with email support.");
        }
        return (IUserEmailStore<RemotelyUser>)UserStore;
    }

    private async Task<bool> IsRegistrationEnabled()
    {
        var organizationCount = DataService.GetOrganizationCount();
        var settings = await DataService.GetSettings();
        return settings.MaxOrganizationCount < 0 || organizationCount < settings.MaxOrganizationCount;
    }

    private sealed class InputModel
    {
        [Required]
        [EmailAddress]
        [Display(Name = "Email")]
        public string Email { get; set; } = "";

        [Required]
        [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public string Password { get; set; } = "";

        [DataType(DataType.Password)]
        [Display(Name = "Confirm password")]
        [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
        public string ConfirmPassword { get; set; } = "";
    }
}
