<script lang="ts">
	import { onMount, tick } from 'svelte';
	import { page } from '$app/state';
	import { api, getBlockedBy, type BlockedByDetail } from '$lib/api';
	import BlockedByModal from '$lib/components/BlockedByModal.svelte';
	import { t, getLocale } from '$lib/i18n';
	import { targetsCache, telegramBotsCache, emailBotsCache, matrixBotsCache } from '$lib/stores/caches.svelte';
	import PageHeader from '$lib/components/PageHeader.svelte';
	import Button from '$lib/components/Button.svelte';
	import Card from '$lib/components/Card.svelte';
	import Loading from '$lib/components/Loading.svelte';
	import MdiIcon from '$lib/components/MdiIcon.svelte';
	import EmptyState from '$lib/components/EmptyState.svelte';
	import ConfirmModal from '$lib/components/ConfirmModal.svelte';
	import IconButton from '$lib/components/IconButton.svelte';
	import CrossLink from '$lib/components/CrossLink.svelte';
	import { chatActionItems } from '$lib/grid-items';
	import { snackSuccess, snackError } from '$lib/stores/snackbar.svelte';
	import { highlightFromUrl } from '$lib/highlight';
	import ErrorBanner from '$lib/components/ErrorBanner.svelte';
	import type { NotificationTarget, TargetReceiver, TelegramChat } from '$lib/types';

	import TargetForm from './TargetForm.svelte';
	import ReceiverSection from './ReceiverSection.svelte';

	// ── Helpers ──

	function getBotName(target: NotificationTarget): string | null {
		if (target.type === 'telegram' && target.config?.bot_id) {
			const bot = telegramBots.find(b => b.id === target.config.bot_id);
			return bot?.name || null;
		}
		if (target.type === 'email' && target.config?.email_bot_id) {
			const bot = emailBots.find(b => b.id === target.config.email_bot_id);
			return bot?.name || null;
		}
		if (target.type === 'matrix' && target.config?.matrix_bot_id) {
			const bot = matrixBots.find(b => b.id === target.config.matrix_bot_id);
			return bot?.name || null;
		}
		return null;
	}

	function getBotHref(target: NotificationTarget): string {
		if (target.type === 'telegram') return '/bots?tab=telegram';
		if (target.type === 'email') return '/bots?tab=email';
		if (target.type === 'matrix') return '/bots?tab=matrix';
		return '/bots?tab=telegram';
	}

	function getBotEntityId(target: NotificationTarget): number | null {
		if (target.type === 'telegram') return target.config?.bot_id || null;
		if (target.type === 'email') return target.config?.email_bot_id || null;
		if (target.type === 'matrix') return target.config?.matrix_bot_id || null;
		return null;
	}

	function receiverLabel(target: NotificationTarget, recv: TargetReceiver): string {
		const c = recv.config || {};
		if (target.type === 'telegram') {
			return recv.chat_name || c.chat_id || recv.receiver_key || '?';
		}
		if (target.type === 'email') return c.email || recv.receiver_key || '?';
		if (target.type === 'webhook') return c.url || recv.receiver_key || '?';
		if (target.type === 'discord' || target.type === 'slack') {
			const url = c.webhook_url || recv.receiver_key || '';
			return url.length > 50 ? url.substring(0, 50) + '...' : url || '?';
		}
		if (target.type === 'ntfy') return c.topic || recv.receiver_key || '?';
		if (target.type === 'matrix') return c.room_id || recv.receiver_key || '?';
		return recv.receiver_key || '?';
	}

	// ── Constants ──

	const ALL_TYPES = ['telegram', 'webhook', 'email', 'discord', 'slack', 'ntfy', 'matrix', 'broadcast'] as const;
	type TargetType = typeof ALL_TYPES[number];
	const TYPE_ICONS: Record<string, string> = {
		telegram: 'mdiSend', webhook: 'mdiWebhook', email: 'mdiEmailOutline',
		discord: 'mdiChat', slack: 'mdiSlack', ntfy: 'mdiBell', matrix: 'mdiMatrix',
		broadcast: 'mdiBullhorn',
	};
	const TYPE_DESC_KEYS: Record<string, string> = {
		telegram: 'targets.descTelegram', webhook: 'targets.descWebhook', email: 'targets.descEmail',
		discord: 'targets.descDiscord', slack: 'targets.descSlack', ntfy: 'targets.descNtfy', matrix: 'targets.descMatrix',
		broadcast: 'targets.descBroadcast',
	};

	const typeGridItems = $derived(ALL_TYPES.map(tt => ({
		value: tt,
		icon: TYPE_ICONS[tt] || 'mdiTarget',
		label: tt.charAt(0).toUpperCase() + tt.slice(1),
	})));

	// ── Derived state ──

	let allTargets = $derived(targetsCache.items);
	let activeType = $derived(page.url.searchParams.get('type') as TargetType | null);
	let filterText = $state('');
	let targets = $derived(allTargets.filter(t =>
		(!activeType || t.type === activeType) &&
		(!filterText || t.name.toLowerCase().includes(filterText.toLowerCase()))
	));
	let telegramBots = $derived(telegramBotsCache.items);
	let emailBots = $derived(emailBotsCache.items);
	let matrixBots = $derived(matrixBotsCache.items);
	const telegramBotItems = $derived(telegramBots.map(b => ({ value: b.id, label: b.name, icon: b.icon || 'mdiRobot', desc: b.bot_username ? `@${b.bot_username}` : '' })));
	const emailBotItems = $derived(emailBots.map(b => ({ value: b.id, label: b.name, icon: b.icon || 'mdiEmailOutline', desc: b.email })));
	const matrixBotItems = $derived(matrixBots.map(b => ({ value: b.id, label: b.name, icon: b.icon || 'mdiMatrix', desc: b.display_name || b.homeserver_url })));

	// ── Target form state ──

	let showForm = $state(false);
	let editing = $state<number | null>(null);
	let formType = $state<TargetType>('telegram');
	const defaultForm = () => ({
		name: '', icon: '', bot_id: 0, bot_token: '',
		max_media_to_send: 50, max_media_per_group: 10, media_delay: 500, max_asset_size: 50,
		disable_url_preview: true, send_large_photos_as_documents: false, ai_captions: false, chat_action: 'typing',
		// Discord/Slack shared settings
		username: '',
		// ntfy shared settings
		server_url: 'https://ntfy.sh', auth_token: '',
		// Matrix
		matrix_bot_id: 0,
		// Email
		email_bot_id: 0,
		// Broadcast
		child_target_ids: [] as number[],
	});
	let form = $state(defaultForm());
	let nameManuallyEdited = $state(false);
	let error = $state('');
	let loaded = $state(false);
	let submitting = $state(false);
	let loadError = $state('');
	let showTelegramSettings = $state(false);
	let confirmDelete = $state<NotificationTarget | null>(null);
	let formEl = $state<HTMLElement | undefined>();

	const TARGET_TYPE_DEFAULT_NAMES: Record<TargetType, string> = {
		telegram: 'Telegram', webhook: 'Webhook', email: 'Email',
		discord: 'Discord', slack: 'Slack', ntfy: 'ntfy', matrix: 'Matrix',
		broadcast: 'Broadcast',
	};
	$effect(() => {
		if (showForm && !nameManuallyEdited && !editing) {
			form.name = TARGET_TYPE_DEFAULT_NAMES[formType] ?? '';
		}
	});

	async function scrollToForm() {
		await tick();
		formEl?.scrollIntoView({ behavior: 'smooth', block: 'start' });
	}

	// ── Receiver inline form state ──

	let addingReceiverForTarget = $state<number | null>(null);
	let receiverForm = $state<Record<string, any>>({});
	let receiverSubmitting = $state(false);
	let receiverBotChats = $state<Record<number, TelegramChat[]>>({});
	let receiverHeadersError = $state('');
	let confirmDeleteReceiver = $state<{ targetId: number; receiver: TargetReceiver } | null>(null);
	let receiverTesting = $state<Record<number, boolean>>({});

	// ── Effects ──

	// Reset form when switching target type tabs
	$effect(() => {
		activeType; // track
		showForm = false;
		editing = null;
		error = '';
		addingReceiverForTarget = null;
	});

	// ── Data loading ──

	onMount(load);

	const headerPills = $derived.by(() => {
		const pills: Array<{ label: string; tone: 'mint' | 'sky' | 'orchid' }> = [];
		if (activeType) {
			// Tab-filtered: show count of receivers for the active type only.
			const total = targets.reduce((acc, t) => acc + (t.receiver_count || 0), 0);
			if (total > 0) pills.push({ label: `${total} ${total === 1 ? t('targets.receiver') : t('targets.receivers')}`, tone: 'mint' });
		} else {
			const types = new Set(targets.map(t => t.type)).size;
			if (types > 0) pills.push({ label: `${types} ${t('targets.channelsCount')}`, tone: 'sky' });
		}
		return pills;
	});

	async function load() {
		try {
			await Promise.all([
				targetsCache.fetch(true), telegramBotsCache.fetch(),
				emailBotsCache.fetch(), matrixBotsCache.fetch(),
			]);
			loadError = '';
		} catch (err: any) {
			loadError = err.message || t('common.loadError');
			snackError(loadError);
		} finally {
			loaded = true;
			highlightFromUrl();
		}
	}

	async function loadReceiverBotChats(botId: number) {
		if (!botId) return;
		try {
			const data = await api<TelegramChat[]>(`/telegram-bots/${botId}/chats`);
			receiverBotChats = { ...receiverBotChats, [botId]: data };
		} catch (e) { console.warn('Failed to load bot chats:', e); }
	}

	// ── Target CRUD ──

	function openNew() {
		form = defaultForm();
		formType = activeType || 'telegram';
		// Auto-select first available bot of the chosen type
		if (formType === 'telegram' && telegramBots.length > 0) form.bot_id = telegramBots[0].id;
		if (formType === 'email' && emailBots.length > 0) form.email_bot_id = emailBots[0].id;
		if (formType === 'matrix' && matrixBots.length > 0) form.matrix_bot_id = matrixBots[0].id;
		nameManuallyEdited = false;
		editing = null;
		showTelegramSettings = false;
		showForm = true;
		scrollToForm();
	}

	async function edit(tgt: NotificationTarget) {
		formType = tgt.type as TargetType;
		const c = tgt.config || {};
		form = {
			name: tgt.name, icon: tgt.icon || '',
			// telegram
			bot_id: c.bot_id || 0, bot_token: '',
			max_media_to_send: c.max_media_to_send ?? 50, max_media_per_group: c.max_media_per_group ?? 10,
			media_delay: c.media_delay ?? 500, max_asset_size: c.max_asset_size ?? 50,
			disable_url_preview: c.disable_url_preview ?? false, send_large_photos_as_documents: c.send_large_photos_as_documents ?? false,
			ai_captions: c.ai_captions ?? false, chat_action: tgt.chat_action ?? c.chat_action ?? 'typing',
			// discord/slack
			username: c.username || '',
			// ntfy
			server_url: c.server_url || 'https://ntfy.sh',
			auth_token: c.auth_token || '',
			// email
			email_bot_id: c.email_bot_id || 0,
			// matrix
			matrix_bot_id: c.matrix_bot_id || 0,
			// broadcast
			child_target_ids: c.child_target_ids || [],
		};
		nameManuallyEdited = true;
		editing = tgt.id;
		showTelegramSettings = false;
		showForm = true;
		scrollToForm();
	}

	async function save(e: SubmitEvent) {
		e.preventDefault();
		error = '';
		if (submitting) return;
		submitting = true;
		try {
			let config: Record<string, any> = {};

			if (formType === 'telegram') {
				let botToken = form.bot_token;
				if (form.bot_id && !botToken) {
					const tokenRes = await api<{ token: string }>(`/telegram-bots/${form.bot_id}/token`);
					botToken = tokenRes.token;
				}
				config = {
					...(botToken ? { bot_token: botToken } : {}),
					bot_id: form.bot_id || undefined,
					max_media_to_send: form.max_media_to_send, max_media_per_group: form.max_media_per_group,
					media_delay: form.media_delay, max_asset_size: form.max_asset_size,
					disable_url_preview: form.disable_url_preview, send_large_photos_as_documents: form.send_large_photos_as_documents,
					ai_captions: form.ai_captions,
				};
			} else if (formType === 'webhook') {
				config = { ai_captions: form.ai_captions };
			} else if (formType === 'discord' || formType === 'slack') {
				config = { username: form.username || undefined };
			} else if (formType === 'ntfy') {
				config = { server_url: form.server_url, auth_token: form.auth_token || undefined };
			} else if (formType === 'email') {
				config = { email_bot_id: form.email_bot_id };
			} else if (formType === 'matrix') {
				config = { matrix_bot_id: form.matrix_bot_id };
			} else if (formType === 'broadcast') {
				config = { child_target_ids: form.child_target_ids };
			}

			const body: Record<string, any> = { name: form.name, icon: form.icon, config };
			if (formType === 'telegram') body.chat_action = form.chat_action || null;
			if (editing) {
				await api(`/targets/${editing}`, { method: 'PUT', body: JSON.stringify(body) });
			} else {
				await api('/targets', { method: 'POST', body: JSON.stringify({ type: formType, ...body }) });
			}
			showForm = false;
			editing = null;
			await load();
			snackSuccess(t('snack.targetSaved'));
		} catch (err: any) {
			error = err.message;
			snackError(err.message);
		} finally {
			submitting = false;
		}
	}

	async function test(id: number) {
		try {
			const res = await api<{ success: boolean; error?: string }>(`/targets/${id}/test?locale=${getLocale()}`, { method: 'POST' });
			if (res.success) snackSuccess(t('snack.targetTestSent'));
			else snackError(`Failed: ${res.error}`);
		} catch (err: any) { snackError(err.message); }
	}

	let blockedBy = $state<BlockedByDetail | null>(null);
	async function remove(id: number) {
		try {
			await api(`/targets/${id}`, { method: 'DELETE' });
			await load();
			snackSuccess(t('snack.targetDeleted'));
		} catch (err: any) {
			const bb = getBlockedBy(err);
			if (bb) { blockedBy = bb; return; }
			error = err.message;
			snackError(err.message);
		}
	}

	// ── Receiver CRUD ──

	function openReceiverForm(targetId: number, targetType: string) {
		addingReceiverForTarget = targetId;
		receiverHeadersError = '';
		if (targetType === 'telegram') {
			receiverForm = { chat_id: '' };
			// Load bot chats for the target's bot
			const tgt = allTargets.find(t => t.id === targetId);
			const botId = tgt?.config?.bot_id;
			if (botId && !receiverBotChats[botId]) loadReceiverBotChats(botId);
		} else if (targetType === 'email') {
			receiverForm = { email: '' };
		} else if (targetType === 'webhook') {
			receiverForm = { url: '', headers: '' };
		} else if (targetType === 'discord' || targetType === 'slack') {
			receiverForm = { webhook_url: '' };
		} else if (targetType === 'ntfy') {
			receiverForm = { topic: '' };
		} else if (targetType === 'matrix') {
			receiverForm = { room_id: '' };
		}
	}

	async function saveReceiver(targetId: number) {
		if (receiverSubmitting) return;
		receiverSubmitting = true;
		receiverHeadersError = '';
		try {
			const config: Record<string, any> = { ...receiverForm };
			// Enrich Telegram receiver with chat metadata
			if (config.chat_id && addingReceiverForTarget) {
				const target = allTargets.find(t => t.id === addingReceiverForTarget);
				const botId = target?.config?.bot_id || target?.config?.telegram_bot_id;
				if (botId && receiverBotChats[botId]) {
					const chat = receiverBotChats[botId].find((c: TelegramChat) => String(c.chat_id) === String(config.chat_id));
					if (chat) {
						config.chat_name = chat.title || chat.username || '';
						if (chat.language_code) config.language_code = chat.language_code;
					}
				}
			}
			// Parse headers JSON for webhook
			if ('headers' in config && typeof config.headers === 'string') {
				if (config.headers) {
					try { config.headers = JSON.parse(config.headers); }
					catch { receiverHeadersError = t('common.headersInvalid'); return; }
				} else {
					delete config.headers;
				}
			}
			await api(`/targets/${targetId}/receivers`, {
				method: 'POST',
				body: JSON.stringify({ name: '', config }),
			});
			addingReceiverForTarget = null;
			await load();
			snackSuccess(t('targets.receiverAdded'));
		} catch (err: any) {
			snackError(err.message);
		} finally {
			receiverSubmitting = false;
		}
	}

	async function toggleReceiver(targetId: number, receiver: TargetReceiver) {
		try {
			await api(`/targets/${targetId}/receivers/${receiver.id}`, {
				method: 'PUT',
				body: JSON.stringify({ enabled: !receiver.enabled }),
			});
			await load();
			snackSuccess(receiver.enabled ? t('targets.receiverDisabled') : t('targets.receiverEnabled'));
		} catch (err: any) { snackError(err.message); }
	}

	async function removeReceiver(targetId: number, receiverId: number) {
		try {
			await api(`/targets/${targetId}/receivers/${receiverId}`, { method: 'DELETE' });
			await load();
			snackSuccess(t('targets.receiverDeleted'));
		} catch (err: any) { snackError(err.message); }
	}

	async function toggleBroadcastChild(targetId: number, childId: number) {
		const tgt = allTargets.find(t => t.id === targetId);
		if (!tgt) return;
		const disabled = new Set<number>(tgt.config?.disabled_child_ids || []);
		if (disabled.has(childId)) disabled.delete(childId);
		else disabled.add(childId);
		try {
			await api(`/targets/${targetId}`, {
				method: 'PUT',
				body: JSON.stringify({ config: { ...tgt.config, disabled_child_ids: [...disabled] } }),
			});
			await load();
		} catch (err: any) { snackError(err.message); }
	}

	async function testReceiver(targetId: number, receiverId: number) {
		receiverTesting = { ...receiverTesting, [receiverId]: true };
		try {
			const res = await api<{ success: boolean; error?: string }>(`/targets/${targetId}/receivers/${receiverId}/test?locale=${getLocale()}`, { method: 'POST' });
			if (res.success) snackSuccess(t('snack.targetTestSent'));
			else snackError(`Failed: ${res.error}`);
		} catch (err: any) { snackError(err.message); }
		finally { receiverTesting = { ...receiverTesting, [receiverId]: false }; }
	}
</script>

<PageHeader
	title={activeType ? activeType.charAt(0).toUpperCase() + activeType.slice(1) : t('targets.title')}
	emphasis={activeType ? t('targets.titleEmphasis') : t('targets.titleEmphasisAll')}
	description={activeType ? t(TYPE_DESC_KEYS[activeType]) : t('targets.description')}
	crumb="Routing · Targets"
	count={targets.length}
	countLabel={t('dashboard.targetsShort')}
	pills={headerPills}
>
	<Button size="sm" onclick={() => { showForm ? (showForm = false, editing = null) : openNew(); }}>
		{showForm ? t('targets.cancel') : t('targets.addTarget')}
	</Button>
</PageHeader>

{#if !loaded}<Loading />{:else}

{#if loadError}
	<ErrorBanner message={loadError} />
{/if}

{#if showForm}
	<div bind:this={formEl}></div>
	<TargetForm
		bind:form
		bind:formType
		{activeType}
		{typeGridItems}
		{telegramBotItems}
		{emailBotItems}
		{matrixBotItems}
		chatActionItems={chatActionItems()}
		telegramBotCount={telegramBots.length}
		emailBotCount={emailBots.length}
		matrixBotCount={matrixBots.length}
		broadcastChildItems={allTargets.filter(t => t.type !== 'broadcast' && t.id !== editing).map(t => ({ value: t.id, label: t.name, icon: t.icon || TYPE_ICONS[t.type] || 'mdiTarget', desc: t.type }))}
		{editing}
		{submitting}
		{error}
		bind:showTelegramSettings
		onsave={save}
		ontoggleTelegramSettings={() => showTelegramSettings = !showTelegramSettings}
		onnameinput={() => nameManuallyEdited = true}
	/>
{/if}

{#if !showForm && allTargets.length > 0}
<div class="flex items-center gap-2 mb-3">
	<input type="text" bind:value={filterText} placeholder={t('common.filterByName')}
		class="flex-1 px-3 py-1.5 border border-[var(--color-border)] rounded-md text-sm bg-[var(--color-background)]" />
</div>
{/if}

{#if allTargets.length === 0 && !showForm}
	<Card>
		<EmptyState icon="mdiTarget" message={t('targets.noTargets')} />
	</Card>
{:else if targets.length === 0 && !showForm}
	<Card>
		<EmptyState icon="mdiFilterOff" message={t('common.noFilterResults')} />
	</Card>
{:else}
	<div class="space-y-3 stagger-children">
		{#each targets as target (target.id)}
			<Card hover entityId={target.id}>
				<!-- Target header -->
				<div class="flex items-center justify-between">
					<div>
						<div class="flex items-center gap-2">
							<span style="color: var(--color-primary);"><MdiIcon name={target.icon || TYPE_ICONS[target.type] || 'mdiTarget'} size={20} /></span>
							<p class="font-medium">{target.name}</p>
							{#if !activeType}<span class="text-xs px-1.5 py-0.5 rounded bg-[var(--color-muted)] text-[var(--color-muted-foreground)]">{target.type}</span>{/if}
							{#if target.type === 'broadcast' && target.child_targets?.length}
								<span class="text-xs px-1.5 py-0.5 rounded bg-[var(--color-muted)] text-[var(--color-muted-foreground)]">{target.child_targets.length} {t('targets.childTargets')}</span>
							{:else if target.type !== 'broadcast' && (target.receivers || []).length > 0}
								<span class="text-xs px-1.5 py-0.5 rounded bg-[var(--color-muted)] text-[var(--color-muted-foreground)]">{(target.receivers || []).length} {t('targets.receivers')}</span>
							{/if}
							{#if getBotName(target)}<CrossLink href={getBotHref(target)} icon="mdiRobot" label={getBotName(target) ?? ''} entityId={getBotEntityId(target)} />{/if}
						</div>
					</div>
					<div class="flex items-center gap-1">
						<IconButton icon="mdiPencil" title={t('common.edit')} onclick={() => edit(target)} />
						<IconButton icon="mdiSend" title={t('targets.test')} onclick={() => test(target.id)} />
						<IconButton icon="mdiDelete" title={t('common.delete')} onclick={() => confirmDelete = target} variant="danger" />
					</div>
				</div>

				<!-- Receivers list -->
				<ReceiverSection
					{target}
					typeIcons={TYPE_ICONS}
					{addingReceiverForTarget}
					bind:receiverForm
					{receiverSubmitting}
					{receiverHeadersError}
					{receiverBotChats}
					{receiverTesting}
					{receiverLabel}
					onopenReceiverForm={openReceiverForm}
					onsaveReceiver={saveReceiver}
					oncancelReceiver={() => addingReceiverForTarget = null}
					ontoggleReceiver={toggleReceiver}
					onremoveReceiver={(targetId, recv) => confirmDeleteReceiver = { targetId, receiver: recv }}
					ontestReceiver={testReceiver}
					onloadBotChats={loadReceiverBotChats}
					onchangeReceiverForm={(f) => receiverForm = f}
					ontoggleBroadcastChild={toggleBroadcastChild}
				/>
			</Card>
		{/each}
	</div>
{/if}

{/if}

<ConfirmModal
	open={!!confirmDelete}
	message={t('targets.confirmDelete')}
	onconfirm={() => { if (confirmDelete) { remove(confirmDelete.id); confirmDelete = null; } }}
	oncancel={() => confirmDelete = null}
/>

<ConfirmModal
	open={!!confirmDeleteReceiver}
	message={t('targets.confirmDeleteReceiver')}
	onconfirm={() => { if (confirmDeleteReceiver) { removeReceiver(confirmDeleteReceiver.targetId, confirmDeleteReceiver.receiver.id); confirmDeleteReceiver = null; } }}
	oncancel={() => confirmDeleteReceiver = null}
/>

<BlockedByModal open={!!blockedBy} detail={blockedBy} onclose={() => blockedBy = null} />
