@page "/Subscribers"
@using Microsoft.EntityFrameworkCore
@using Microsoft.Extensions.Options
@using Wave.Data
@using Wave.Utilities
@using CsvHelper.Configuration
@using System.Globalization
@using CsvHelper

@attribute [Authorize(Roles = "Admin")]

@inject IStringLocalizer<Subscribers> Localizer
@inject IDbContextFactory<ApplicationDbContext> ContextFactory
@inject ILogger<Subscribers> Logger
@inject IMessageDisplay Message

<ModalComponent Id="@ModalId">
	<ChildContent>
		<form id="AddSubscribers" method="post" @formname="AddSubscribers" @onsubmit="AddSubscribers">
			<AntiforgeryToken/>
			
			<h2 class="text-xl">@Localizer["AddSubscribers_Label"]</h2>
			<span class="my-3"><small>Format: Email;[Name];[Exclusion Reason];</small></span>
			<InputLabelComponent LabelText="@Localizer["AddSubscribers_Input_Label"]">
				<InputTextArea class="textarea textarea-bordered" rows="12"
				               @bind-Value="@SubscribersInput"
				               placeholder="@Localizer["AddSubscribers_Input_Placeholder"]"
				               required aria-required max="8096"
				               autocomplete="off"/>
			</InputLabelComponent>
		</form>
	</ChildContent>
	<Actions>
		<button type="submit" form="AddSubscribers" class="btn btn-primary">
			@Localizer["Submit"]
		</button>
	</Actions>
</ModalComponent>

<PageTitle>@(Localizer["Title"] + TitlePostfix)</PageTitle>

<h1 class="text-3xl lg:text-5xl font-light mb-6 text-primary">@Localizer["Title"]</h1>

<div class="flex flex-wrap gap-2 mb-3">
	<details class="dropdown">
		<summary class="btn btn-sm btn-accent">
			@Items / 
			<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-4 h-4" title="page">
				<path d="M5.625 1.5c-1.036 0-1.875.84-1.875 1.875v17.25c0 1.035.84 1.875 1.875 1.875h12.75c1.035 0 1.875-.84 1.875-1.875V12.75A3.75 3.75 0 0 0 16.5 9h-1.875a1.875 1.875 0 0 1-1.875-1.875V5.25A3.75 3.75 0 0 0 9 1.5H5.625Z" />
				<path d="M12.971 1.816A5.23 5.23 0 0 1 14.25 5.25v1.875c0 .207.168.375.375.375H16.5a5.23 5.23 0 0 1 3.434 1.279 9.768 9.768 0 0 0-6.963-6.963Z" />
			</svg>
		</summary>
		<ul class="p-2 shadow-xl menu dropdown-content z-[1] bg-accent text-accent-content rounded-box w-52">
			<li><a href="@($"/subscribers?page={Math.Floor(Items * Page / 5.0)}&items=5")">5</a></li>
			<li><a href="@($"/subscribers?page={Math.Floor(Items * Page / 10.0)}")">10</a></li>
			<li><a href="@($"/subscribers?page={Math.Floor(Items * Page / 25.0)}&items=25")">25</a></li>
			<li><a href="@($"/subscribers?page={Math.Floor(Items * Page / 50.0)}&items=50")">50</a></li>
			<li><a href="@($"/subscribers?page={Math.Floor(Items * Page / 100.0)}&items=100")">100</a></li>
			<li><a href="@($"/subscribers?page={Math.Floor(Items * Page / 250.0)}&items=250")">250</a></li>
		</ul>
	</details>

	<button class="btn btn-sm btn-primary" onclick="@(ModalId).showModal()">
		@Localizer["AddSubscribers_Label"]
	</button>
</div>

<section class="flex flex-col h-128">
	<div class="flex-grow overflow-auto">
		<table class="table relative">
			<thead>
				<tr>
					<th class="bg-base-200 sticky top-0 text-center md:text-left md:w-48">@Localizer["Header_Email"]</th>
					<th class="bg-base-200 sticky top-0 text-center md:text-left w-48 max-md:hidden">@Localizer["Header_Name"]</th>
					<th class="bg-base-200 sticky top-0 text-center md:text-left w-24 max-md:hidden">@Localizer["Header_LastReceived"]</th>
					<th class="bg-base-200 sticky top-0 text-center md:text-left w-24 max-md:hidden">@Localizer["Header_LastOpen"]</th>
					<th class="bg-base-200 sticky top-0 text-center md:text-left w-24 max-md:hidden">@Localizer["Header_UnsubscribeReason"]</th>
					<th class="bg-base-200 sticky top-0 text-center md:text-left md:w-8 z-10">@Localizer["Header_Subscribed"]</th>
					<td class="bg-base-200 sticky top-0 text-center md:text-left w-24"></td>
				</tr>
			</thead>
			<tbody class="divide-y">
			<PageComponent Page="@Page" LoadCallback="LoadSubscribers" ItemsPerPage="@Items">
				<tr>
					<td>@context.Email</td>
					<td class="max-md:hidden">@context.Name</td>
					<td class="max-md:hidden">@context.LastMailReceived?.ToString("g")</td>
					<td class="max-md:hidden">@context.LastMailOpened?.ToString("g")</td>
					<td class="max-md:hidden">@context.UnsubscribeReason</td>
					<td class="text-center md:text-left">
						<input type="checkbox" class="checkbox no-animation" checked="@(!context.Unsubscribed)" disabled/>
					</td>
					<td>
						<a class="btn btn-sm btn-info w-24" href="@($"/subscribers/edit/{context.Id}")">
							@Localizer["Subscriber_Edit"]
						</a>
					</td>
				</tr>
			</PageComponent>
			</tbody>
			<tfoot>
			<tr>
				<td colspan="6" class="max-md:hidden">
					@Localizer["Newsletter_Footer_Timezone"] @TimeZoneInfo.Local
				</td>
			</tr>
			</tfoot>
		</table>
	</div>
	
	<div class="grid place-content-center my-3">
		<div class="join">
			@if (Page < 1) {
				<button class="join-item btn" disabled title="@Localizer["Paging_Previous"]">
					<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6">
						<path fill-rule="evenodd" d="M7.72 12.53a.75.75 0 0 1 0-1.06l7.5-7.5a.75.75 0 1 1 1.06 1.06L9.31 12l6.97 6.97a.75.75 0 1 1-1.06 1.06l-7.5-7.5Z" clip-rule="evenodd" />
					</svg>
				</button>
			} else {
				<a class="join-item btn" target="_top" href="@(Page < 2 ? $"/subscribers?items={Items}" : $"/subscribers?page={Page - 1}&items={Items}")" title="@Localizer["Paging_Previous"]">
					<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6">
						<path fill-rule="evenodd" d="M7.72 12.53a.75.75 0 0 1 0-1.06l7.5-7.5a.75.75 0 1 1 1.06 1.06L9.31 12l6.97 6.97a.75.75 0 1 1-1.06 1.06l-7.5-7.5Z" clip-rule="evenodd"/>
					</svg>
				</a>
			}
			<button class="join-item btn md:btn-wide no-animation">@Localizer["Paging_Page"] @(Page + 1)</button>
			@if (Page >= TotalPages - 1) {
				<button class="join-item btn" disabled title="@Localizer["Paging_Next"]">
					<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6">
						<path fill-rule="evenodd" d="M16.28 11.47a.75.75 0 0 1 0 1.06l-7.5 7.5a.75.75 0 0 1-1.06-1.06L14.69 12 7.72 5.03a.75.75 0 0 1 1.06-1.06l7.5 7.5Z" clip-rule="evenodd"/>
					</svg>
				</button>
			} else {
				<a class="join-item btn" target="_top" href="@($"/subscribers?page={Page + 1}&items={Items}")" title="@Localizer["Paging_Next"]">
					<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6">
						<path fill-rule="evenodd" d="M16.28 11.47a.75.75 0 0 1 0 1.06l-7.5 7.5a.75.75 0 0 1-1.06-1.06L14.69 12 7.72 5.03a.75.75 0 0 1 1.06-1.06l7.5 7.5Z" clip-rule="evenodd"/>
					</svg>
				</a>
			}
		</div>
	</div>
</section>

@code {
	[CascadingParameter(Name = "TitlePostfix")]
	private string TitlePostfix { get; set; } = default!;
	[SupplyParameterFromQuery]
	public int Page { get; set; } = 0;
	[SupplyParameterFromQuery]
	public int Items { get; set; }

	private int TotalPages { get; set; }

	[SupplyParameterFromForm(FormName = "AddSubscribers")]
	private string SubscribersInput { get; set; } = string.Empty;
	private static string ModalId => "AddSubscribersDialog";

	protected override async Task OnInitializedAsync() {
		if (Items < 1) Items = 10;
		await using var context = await ContextFactory.CreateDbContextAsync();
		var query = context.Set<EmailSubscriber>().IgnoreQueryFilters();
		TotalPages = (int)Math.Max(Math.Ceiling(await query.CountAsync() / (double)Items), 1);
	}

	private async ValueTask<IEnumerable<EmailSubscriber>> LoadSubscribers(int page, int count) {
		try {
			await using var context = await ContextFactory.CreateDbContextAsync();
			return await context.Set<EmailSubscriber>()
				.IgnoreAutoIncludes().IgnoreQueryFilters()
				.OrderBy(s => s.Email).ThenBy(s => s.Id)
				.Skip(page).Take(count).ToListAsync();
		} catch (Exception ex) {
			Logger.LogError(ex, "Failed to load subscribers on page {Page} with count {Count}.", page, count);
			Message.ShowError(Localizer["Subscriber_Load_Error"]);
			return [];
		}
	}

	private async Task AddSubscribers() {
		var config = new CsvConfiguration(CultureInfo.CurrentCulture) {
			NewLine = Environment.NewLine,
			HasHeaderRecord = false
		};

		List<SubscriberModel> list;
		try {
			using var reader = new CsvReader(new StringReader(SubscribersInput), config);
			list = reader.GetRecords<SubscriberModel>().ToList();
		} catch (Exception ex) {
			Message.ShowError(string.Format(Localizer["AddSubscribers_Parse_Error"], ex.Message));
			return;
		}
		if (list.Count < 1) return;

		try {
			var emailSubscribers = new List<EmailSubscriber>();
			foreach (var input in list) {
				emailSubscribers.Add(new EmailSubscriber {
					Email = input.Email,
					Name = input.Name,
					Unsubscribed = !string.IsNullOrWhiteSpace(input.UnsubscribeReason),
					UnsubscribeReason = input.UnsubscribeReason,
					Language = "en-US"
				});
			}

			await using var context = await ContextFactory.CreateDbContextAsync();
			context.AddRange(emailSubscribers);
			await context.SaveChangesAsync();
			SubscribersInput = string.Empty;
		} catch (Exception ex) {
			Message.ShowError(string.Format(Localizer["AddSubscribers_Save_Error"], ex.InnerException?.Message ?? ex.Message));
		}
	}

	internal sealed class SubscriberModel {
		[CsvHelper.Configuration.Attributes.Index(0)]
		public string Email { get; set; } = string.Empty;
		[CsvHelper.Configuration.Attributes.Index(1)]
		public string? Name { get; set; }
		[CsvHelper.Configuration.Attributes.Index(2)]
		public string? UnsubscribeReason { get; set; }
	}
}
