@page "/Account/Register"

@using System.ComponentModel.DataAnnotations
@using System.Text
@using System.Text.Encodings.Web
@using Microsoft.AspNetCore.Identity
@using Microsoft.AspNetCore.WebUtilities
@using Microsoft.Extensions.Options
@using Wave.Data

@inject IOptions<Features> Features
@inject UserManager<ApplicationUser> UserManager
@inject IUserStore<ApplicationUser> UserStore
@inject SignInManager<ApplicationUser> SignInManager
@inject IEmailSender<ApplicationUser> EmailSender
@inject ILogger<Register> Logger
@inject NavigationManager NavigationManager
@inject IdentityRedirectManager RedirectManager
@inject IStringLocalizer<Register> Localizer
@inject NavigationManager Navigation

<HeadContent>
	<meta name="robots" content="nofollow">
	<link rel="canonical" href="@(new UriBuilder(Navigation.BaseUri) { Scheme = "https", Port = -1, Path = "/account/register" }.Uri.AbsoluteUri)" />
</HeadContent>

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

<StatusMessage Message="@Message" />

<BoardComponent CenterContent="true">
	<BoardCardComponent Heading="@Localizer["Title"]">
		@if (Features.Value.NativeSignup) {
			<EditForm Model="Input" asp-route-returnUrl="@ReturnUrl" method="post" OnValidSubmit="RegisterUser" 
					  FormName="register" class="w-full">
				<DataAnnotationsValidator />

				<InputLabelComponent LabelText="@Localizer["Email_Label"]" For="() => Input.Email">
					<InputText @bind-Value="Input.Email" class="input input-bordered w-full" autocomplete="username" 
							   required aria-required="true" placeholder="@Localizer["Email_Placeholder"]" />
				</InputLabelComponent>
				<InputLabelComponent LabelText="@Localizer["Password_Label"]" For="() => Input.Password">
					<InputText @bind-Value="Input.Password" class="input input-bordered w-full" 
							   autocomplete="new-password" type="password"
							   required aria-required="true" placeholder="@Localizer["Password_Placeholder"]" />
				</InputLabelComponent>
				<InputLabelComponent LabelText="@Localizer["ConfirmPassword_Label"]" For="() => Input.ConfirmPassword">
					<InputText @bind-Value="Input.ConfirmPassword" class="input input-bordered w-full" 
							   autocomplete="new-password" type="password"
							   required aria-required="true" placeholder="@Localizer["ConfirmPassword_Placeholder"]"/>
				</InputLabelComponent>

				<button type="submit" class="btn btn-primary w-full">
					@Localizer["Submit"]
				</button>
			</EditForm>
		} else {
			<Alert CanRemove="false" Type="Alert.MessageType.Warning">
				<p><strong>@Localizer["SignupDisabled_Message"]</strong></p>
			</Alert>
		}
	</BoardCardComponent>
</BoardComponent>

@code {
	private IEnumerable<IdentityError>? _identityErrors;

	[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))}";

	public async Task RegisterUser(EditContext editContext) {
		if (Features.Value.NativeSignup is false) 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 ApplicationUser CreateUser() {
		try {
			return Activator.CreateInstance<ApplicationUser>();
		} catch {
			throw new InvalidOperationException(
				$"Can't create an instance of '{nameof(ApplicationUser)}'. " + 
				$"Ensure that '{nameof(ApplicationUser)}' is not an abstract class and has a parameterless constructor.");
		}
	}

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

		return (IUserEmailStore<ApplicationUser>)UserStore;
	}

	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; } = "";
	}

}
