<script lang="ts">
	import { apiMutate } from '$lib/api/client.js';
	import type { BgpCommunity, CdnSource } from '$lib/api/types.js';
	import { formatDateTime } from '$lib/modules/display.js';
	import {
		communityLabel,
		normalizeCdnSourceKind
	} from '$lib/components/modules/module-helpers.js';
	import { Badge } from '$lib/ui/core/badge/index.js';
	import { Button } from '$lib/ui/core/button/index.js';
	import { Checkbox } from '$lib/ui/core/checkbox/index.js';
	import {
		Card,
		CardContent,
		CardHeader,
		CardTitle,
		CardDescription
	} from '$lib/ui/core/card/index.js';
	import AppDataTable from '$lib/ui/patterns/data-table/app-data-table.svelte';
	import { confirm } from '$lib/ui/patterns/confirm/confirm-state.svelte.js';
	import { notify, notifyApiError } from '$lib/ui/app/toast.js';
	import ModuleCdnSourceDialog from '$lib/components/modules/ModuleCdnSourceDialog.svelte';
	import Plus from '@lucide/svelte/icons/plus';
	import Pencil from '@lucide/svelte/icons/pencil';
	import Trash2 from '@lucide/svelte/icons/trash-2';

	type Props = {
		moduleId: string;
		sources: CdnSource[];
		communities: BgpCommunity[];
		loading?: boolean;
		onChanged: () => void | Promise<void>;
	};

	let { moduleId, sources, communities, loading = false, onChanged }: Props = $props();

	let dialogOpen = $state(false);
	let editTarget = $state<CdnSource | null>(null);
	let selectedIds = $state(new Set<string>());
	let deletingBulk = $state(false);

	const activeSelected = $derived.by(() => {
		const allowed = new Set(sources.map((s) => s.id));
		return [...selectedIds].filter((id) => allowed.has(id));
	});
	const selectedCount = $derived(activeSelected.length);
	const allSelected = $derived(sources.length > 0 && sources.every((s) => selectedIds.has(s.id)));

	const columns = [
		{ id: 'select', label: '', class: 'w-10' },
		{ id: 'url', label: 'URL', sortable: true, sortValue: (s: CdnSource) => s.url },
		{ id: 'kind', label: 'Тип', sortable: true, sortValue: (s: CdnSource) => s.source_kind },
		{ id: 'community', label: 'Community' },
		{
			id: 'interval',
			label: 'Интервал',
			sortable: true,
			sortValue: (s: CdnSource) => s.refresh_interval_sec ?? 0
		},
		{
			id: 'refreshed',
			label: 'Последнее обновление',
			sortable: true,
			sortValue: (s: CdnSource) => s.last_refreshed_at ?? ''
		},
		{ id: 'actions', label: '', class: 'w-20' }
	] as const;

	function toggleSelection(id: string) {
		const next = new Set(selectedIds);
		if (next.has(id)) next.delete(id);
		else next.add(id);
		selectedIds = next;
	}

	function toggleAll(checked: boolean) {
		selectedIds = checked ? new Set(sources.map((s) => s.id)) : new Set<string>();
	}

	function openCreate() {
		editTarget = null;
		dialogOpen = true;
	}

	function openEdit(src: CdnSource) {
		editTarget = src;
		dialogOpen = true;
	}

	function requestDelete(src: CdnSource) {
		void confirm({
			title: 'Удалить CDN-источник?',
			description: src.url,
			confirmLabel: 'Удалить',
			destructive: true,
			onConfirm: async () => {
				await apiMutate(`/v1/modules/${moduleId}/cdn-sources/${src.id}`, 'DELETE', undefined, {
					idempotent: false
				});
				notify.success('Удалено');
				await onChanged();
			}
		});
	}

	function requestBulkDelete() {
		if (selectedCount === 0) return;
		void confirm({
			title: 'Удалить выбранные CDN-источники?',
			description: `Будет удалено: ${selectedCount}`,
			confirmLabel: 'Удалить',
			destructive: true,
			onConfirm: bulkDelete
		});
	}

	async function bulkDelete() {
		if (selectedCount === 0) return;
		deletingBulk = true;
		let deleted = 0;
		try {
			for (const id of activeSelected) {
				try {
					await apiMutate(`/v1/modules/${moduleId}/cdn-sources/${id}`, 'DELETE', undefined, {
						idempotent: false
					});
					deleted += 1;
				} catch (e) {
					notifyApiError(e);
				}
			}
			if (deleted > 0) notify.success(`Удалено CDN-источников: ${deleted}`);
			await onChanged();
		} finally {
			deletingBulk = false;
		}
	}
</script>

<Card>
	<CardHeader class="flex flex-col gap-3 pb-2 sm:flex-row sm:items-center sm:justify-between">
		<div class="min-w-0 flex-1">
			<CardTitle class="text-base">CDN-источники</CardTitle>
			<CardDescription>URL источников для скачивания списков CIDR.</CardDescription>
		</div>
		<div class="flex shrink-0 flex-wrap items-center justify-end gap-2 self-start sm:self-auto">
			<Button size="sm" onclick={openCreate}><Plus />Добавить</Button>
			{#if selectedCount > 0}
				<Button variant="destructive" size="sm" onclick={requestBulkDelete} disabled={deletingBulk}>
					<Trash2 />
					Удалить ({selectedCount})
				</Button>
			{/if}
		</div>
	</CardHeader>
	<CardContent class="p-4 pt-0">
		<AppDataTable
			columns={[...columns]}
			rows={sources}
			rowKey={(s) => s.id}
			{loading}
			emptyTitle="Нет CDN-источников"
			emptyDescription="Добавьте URL для загрузки списков CIDR."
		>
			{#snippet toolbar()}
				{#if sources.length > 0}
					<div class="flex items-center gap-2">
						<Checkbox
							checked={allSelected}
							onCheckedChange={(v) => toggleAll(v === true)}
							aria-label="Выбрать все CDN-источники"
						/>
						<span class="text-sm text-muted-foreground">Выбрать все</span>
					</div>
				{/if}
			{/snippet}
			{#snippet cell({ row: src, column })}
				{#if column.id === 'select'}
					<Checkbox
						checked={selectedIds.has(src.id)}
						aria-label="Выбрать CDN-источник"
						onCheckedChange={() => toggleSelection(src.id)}
					/>
				{:else if column.id === 'url'}
					<span class="max-w-xs truncate font-mono text-xs" title={src.url}>{src.url}</span>
				{:else if column.id === 'kind'}
					<div class="flex flex-col gap-0.5">
						<Badge variant="outline">{normalizeCdnSourceKind(src.source_kind)}</Badge>
						{#if src.prefix_path?.trim()}
							<span
								class="font-mono text-xs break-all text-muted-foreground"
								title={src.prefix_path}>{src.prefix_path}</span
							>
						{/if}
					</div>
				{:else if column.id === 'community'}
					<span class="text-sm text-muted-foreground">
						{communityLabel(src.community_id, communities)}
					</span>
				{:else if column.id === 'interval'}
					<span class="text-sm text-muted-foreground">
						{src.refresh_interval_sec != null ? `${src.refresh_interval_sec}с` : '—'}
					</span>
				{:else if column.id === 'refreshed'}
					<span class="text-sm whitespace-nowrap text-muted-foreground">
						{formatDateTime(src.last_refreshed_at)}
					</span>
				{:else if column.id === 'actions'}
					<div class="flex gap-1">
						<Button variant="ghost" size="icon-sm" onclick={() => openEdit(src)}>
							<Pencil class="size-3.5" />
						</Button>
						<Button
							variant="ghost"
							size="icon-sm"
							class="text-destructive"
							onclick={() => requestDelete(src)}
						>
							<Trash2 class="size-3.5" />
						</Button>
					</div>
				{/if}
			{/snippet}
		</AppDataTable>
	</CardContent>
</Card>

<ModuleCdnSourceDialog
	bind:open={dialogOpen}
	{moduleId}
	edit={editTarget}
	{communities}
	onSaved={onChanged}
	onClose={() => {
		editTarget = null;
	}}
/>
