<script lang="ts">
	import { onMount, onDestroy } from 'svelte';
	import { api, parseDate , errMsg} from '$lib/api';
	import { topbarAction } from '$lib/stores/topbar-action.svelte';
	import { t, getLocale } from '$lib/i18n';
	import { providersCache, targetsCache, trackingConfigsCache, templateConfigsCache, capabilitiesCache } from '$lib/stores/caches.svelte';
	import PageHeader from '$lib/components/PageHeader.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 EntitySelect from '$lib/components/EntitySelect.svelte';
	import { snackSuccess, snackError } from '$lib/stores/snackbar.svelte';
	import { highlightFromUrl } from '$lib/highlight';
	import { providerDefaultIcon } from '$lib/grid-items';
	import { globalProviderFilter } from '$lib/stores/provider-filter.svelte';
	import { getDescriptor } from '$lib/providers';
	import Button from '$lib/components/Button.svelte';
	import ErrorBanner from '$lib/components/ErrorBanner.svelte';
	import type { Tracker, TrackerTarget, TrackingConfig, TemplateConfig, NotificationTarget } from '$lib/types';

	import MetaStrip, { type MetaTile } from '$lib/components/MetaStrip.svelte';
	import TrackerForm from './TrackerForm.svelte';
	import LinkedTargetsSection from './LinkedTargetsSection.svelte';
	import SharedLinkModal from './SharedLinkModal.svelte';
	import TestMenu from './TestMenu.svelte';

	let loaded = $state(false);
	let loadError = $state('');
	let allNotificationTrackers = $state<Tracker[]>([]);
	let filterText = $state('');
	let filterProviderId = $state(0);
	let effectiveProviderId = $derived(globalProviderFilter.id || filterProviderId);
	let notificationTrackers = $derived(allNotificationTrackers.filter(t =>
		(!filterText || t.name.toLowerCase().includes(filterText.toLowerCase())) &&
		(!effectiveProviderId || t.provider_id === effectiveProviderId)
	));
	let providers = $derived(providersCache.items);
	let allCapabilities: Record<string, any> = $derived(capabilitiesCache.items || {});
	const providerItems = $derived(providers
		.filter(p => !globalProviderFilter.providerType || p.type === globalProviderFilter.providerType)
		.map(p => ({ value: p.id, label: p.name, icon: providerDefaultIcon(p), desc: p.type })));
	let targets = $derived(targetsCache.items);
	let trackingConfigs = $derived(trackingConfigsCache.items);
	let templateConfigs = $derived(templateConfigsCache.items);
	let collections = $state<Record<string, any>[]>([]);
	let users = $state<{ id: string; name: string }[]>([]);
	let showForm = $state(false);
	let editing = $state<number | null>(null);
	let collectionFilter = $state('');
	let submitting = $state(false);
	let confirmDelete = $state<Tracker | null>(null);
	let toggling = $state<Record<number, boolean>>({});
	let ttTesting = $state<Record<string, string>>({});

	// Shared link validation
	let linkWarning = $state<{ albums: { id: string; name: string; issue: string }[], providerId: number } | null>(null);
	let linkCheckLoading = $state(false);
	let linkCreating = $state(false);
	let previousCollectionIds = $state<string[]>([]);

	// Tracker form
	const defaultForm = () => ({
		name: '', icon: '', provider_id: 0, collection_ids: [] as string[],
		scan_interval: 60,
		adaptive_max_skip: null as number | null,
		default_tracking_config_id: 0, default_template_config_id: 0,
		filters: {} as Record<string, any>,
	});
	let form = $state(defaultForm());
	let nameManuallyEdited = $state(false);
	let selectedProviderType = $derived(
		providers.find(p => p.id === form.provider_id)?.type || ''
	);
	let error = $state('');

	$effect(() => {
		if (showForm && !nameManuallyEdited && !editing) {
			const provider = providers.find(p => p.id === form.provider_id);
			form.name = provider ? `${provider.name} Tracker` : 'Tracker';
		}
	});

	// Linked targets management
	let expandedTracker = $state<number | null>(null);
	let addingTarget = $state<Record<number, boolean>>({});
	let newLinkTargetId = $state<Record<number, number>>({});
	let newLinkTrackingConfigId = $state<Record<number, number>>({});
	let newLinkTemplateConfigId = $state<Record<number, number>>({});

	// Test menu
	let testMenuOpen = $state<string | null>(null);
	let testMenuStyle = $state('');

	// Test types: basic is always available; periodic/scheduled/memory only for providers
	// that have those notification slots in their capabilities AND have the feature
	// enabled on the tracker's default TrackingConfig. A disabled feature on the
	// default config means cron dispatch won't fire it in production either — so
	// the test button would just surface a silent skip.
	const allTestTypes: Record<string, {
		key: string; icon: string; labelKey: string;
		requiredSlot?: string; enabledField?: string;
	}> = {
		basic: { key: 'basic', icon: 'mdiSend', labelKey: 'notificationTracker.testBasic' },
		periodic: { key: 'periodic', icon: 'mdiCalendarClock', labelKey: 'notificationTracker.testPeriodic', requiredSlot: 'periodic_summary_message', enabledField: 'periodic_enabled' },
		scheduled: { key: 'scheduled', icon: 'mdiCalendarCheck', labelKey: 'notificationTracker.testScheduled', requiredSlot: 'scheduled_assets_message', enabledField: 'scheduled_enabled' },
		memory: { key: 'memory', icon: 'mdiHistory', labelKey: 'notificationTracker.testMemory', requiredSlot: 'memory_mode_message', enabledField: 'memory_enabled' },
	};

	let testMenuTrackerId = $state<number | null>(null);
	let testTypes = $derived.by(() => {
		const base: { key: string; icon: string; labelKey: string; disabledReason?: string }[] = [allTestTypes.basic];
		if (!testMenuTrackerId) return base;
		const tracker = notificationTrackers.find(t => t.id === testMenuTrackerId);
		if (!tracker) return base;
		const provider = providers.find(p => p.id === tracker.provider_id);
		if (!provider) return base;
		const caps = allCapabilities[provider.type];
		if (!caps) return base;
		const slotNames = new Set((caps.notification_slots || []).map((s: any) => s.name));
		const defaultTc = trackingConfigs.find(c => c.id === tracker.default_tracking_config_id);
		for (const tt of [allTestTypes.periodic, allTestTypes.scheduled, allTestTypes.memory]) {
			if (!tt.requiredSlot || !slotNames.has(tt.requiredSlot)) continue;
			const enabled = !!defaultTc && !!tt.enabledField && !!(defaultTc as any)[tt.enabledField];
			base.push({
				key: tt.key, icon: tt.icon, labelKey: tt.labelKey,
				// When surfaced, the button still renders but is disabled and
				// shows *why* — users who land here via the test menu without
				// having toggled the feature on Tracking Config see a clear
				// pointer to the missing setting instead of a silent failure.
				disabledReason: enabled ? undefined : 'notificationTracker.testDisabledHint',
			});
		}
		return base;
	});

	onMount(() => {
		topbarAction.set({
			label: t('notificationTracker.newTracker'),
			onclick: () => { showForm ? (showForm = false, editing = null) : openNew(); },
		});
		load();
	});
	onDestroy(() => topbarAction.clear());

	const headerPills = $derived.by(() => {
		const pills: Array<{ label: string; tone: 'mint' | 'sky' | 'coral' | 'citrus' }> = [];
		const armed = notificationTrackers.filter(t => t.enabled).length;
		const paused = notificationTrackers.length - armed;
		if (armed > 0) pills.push({ label: `${armed} ${t('notificationTracker.armed')}`, tone: 'mint' });
		if (paused > 0) pills.push({ label: `${paused} ${t('notificationTracker.paused')}`, tone: 'citrus' });
		const providerCount = new Set(notificationTrackers.map(t => t.provider_id)).size;
		if (providerCount > 0) pills.push({ label: `${providerCount} ${providerCount === 1 ? t('providers.typeSingular') : t('providers.typePlural')}`, tone: 'sky' });
		return pills;
	});

	async function load() {
		loadError = '';
		try {
			[allNotificationTrackers] = await Promise.all([
				api<Tracker[]>('/notification-trackers'),
				providersCache.fetch(), targetsCache.fetch(),
				trackingConfigsCache.fetch(), templateConfigsCache.fetch(),
				capabilitiesCache.fetch(),
			]);
		} catch (err: unknown) {
			loadError = errMsg(err, t('common.loadFailed'));
			snackError(loadError);
		} finally { loaded = true; highlightFromUrl(); }
	}

	async function loadCollections() {
		if (!form.provider_id) return;
		try { collections = await api(`/providers/${form.provider_id}/collections`); } catch (e) { console.warn('Failed to load collections:', e); collections = []; }
	}

	async function loadUsers() {
		if (!form.provider_id) { users = []; return; }
		// Skip the fetch when the descriptor has no user filters — saves a
		// pointless round-trip for providers like Immich/Scheduler.
		const desc = getDescriptor(selectedProviderType);
		if (!desc?.userFilters || desc.userFilters.length === 0) { users = []; return; }
		try { users = await api(`/providers/${form.provider_id}/users`); }
		catch (e) { console.warn('Failed to load users:', e); users = []; }
	}

	let _prevProviderId = $state(0);
	$effect(() => {
		if (showForm && form.provider_id && form.provider_id !== _prevProviderId) {
			_prevProviderId = form.provider_id;
			loadCollections();
			loadUsers();
			// Re-pick tracking/template configs for the new provider type. The
			// previously-selected ids may belong to a different provider type
			// and therefore no longer appear in the filtered EntitySelect list,
			// which would render the selector as empty.
			if (editing === null) {
				const ptype = providers.find(p => p.id === form.provider_id)?.type || '';
				if (ptype) {
					const currentTc = trackingConfigs.find(c => c.id === form.default_tracking_config_id);
					if (!currentTc || currentTc.provider_type !== ptype) {
						const first = trackingConfigs.find(c => c.provider_type === ptype);
						form.default_tracking_config_id = first?.id ?? 0;
					}
					const currentTpl = templateConfigs.find(c => c.id === form.default_template_config_id);
					if (!currentTpl || currentTpl.provider_type !== ptype) {
						const first = templateConfigs.find(c => c.provider_type === ptype);
						form.default_template_config_id = first?.id ?? 0;
					}
				}
			}
		}
	});

	function openNew() {
		form = defaultForm();
		// Auto-select first provider if any
		if (providers.length > 0) form.provider_id = providers[0].id;
		nameManuallyEdited = false;
		editing = null; showForm = true; collections = []; users = []; previousCollectionIds = [];
	}

	async function edit(trk: Tracker) {
		form = {
			name: trk.name, icon: trk.icon || '', provider_id: trk.provider_id,
			collection_ids: [...(trk.collection_ids || [])],
			scan_interval: trk.scan_interval,
			adaptive_max_skip: trk.adaptive_max_skip ?? null,
			default_tracking_config_id: trk.default_tracking_config_id ?? 0,
			default_template_config_id: trk.default_template_config_id ?? 0,
			filters: trk.filters || {},
		};
		previousCollectionIds = [...(trk.collection_ids || [])];
		nameManuallyEdited = true;
		editing = trk.id; showForm = true;
		if (form.provider_id) {
			await Promise.all([loadCollections(), loadUsers()]);
		}
	}

	async function save(e: SubmitEvent) {
		e.preventDefault(); error = '';
		if (submitting) return;

		// Delegate provider-specific pre-save checks to the descriptor
		const desc = getDescriptor(selectedProviderType);
		if (desc?.onBeforeSave && form.provider_id) {
			linkCheckLoading = true;
			try {
				const result = await desc.onBeforeSave({
					form, previousCollectionIds, collections, api,
				});
				if (!result.proceed) {
					if (result.warnings?.length) {
						linkWarning = { albums: result.warnings, providerId: form.provider_id };
					}
					linkCheckLoading = false;
					return;
				}
			} catch (err) { console.warn('Pre-save check failed, proceeding:', err); }
			linkCheckLoading = false;
		}

		await doSave();
	}

	async function doSave() {
		submitting = true;
		try {
			const payload = {
				...form,
				default_tracking_config_id: form.default_tracking_config_id || null,
				default_template_config_id: form.default_template_config_id || null,
				// Empty string, 0, or null all mean "disable adaptive polling".
				// Coerce to null so the DB column stays NULL rather than 0.
				adaptive_max_skip:
					form.adaptive_max_skip && form.adaptive_max_skip > 1
						? form.adaptive_max_skip
						: null,
			};
			if (editing) {
				await api(`/notification-trackers/${editing}`, { method: 'PUT', body: JSON.stringify(payload) });
				snackSuccess(t('snack.trackerUpdated'));
			} else {
				await api('/notification-trackers', { method: 'POST', body: JSON.stringify(payload) });
				snackSuccess(t('snack.trackerCreated'));
			}
			showForm = false; editing = null; linkWarning = null; await load();
		} catch (err: unknown) { const __m = errMsg(err); error = __m; snackError(__m); } finally { submitting = false; }
	}

	async function autoCreateLinks() {
		if (!linkWarning) return;
		linkCreating = true;
		let created = 0;
		for (const album of linkWarning.albums) {
			if (album.issue === 'missing') {
				try {
					await api(`/providers/${linkWarning.providerId}/albums/${album.id}/shared-links`, { method: 'POST' });
					created++;
				} catch (err: unknown) {
					snackError(`Failed to create link for "${album.name}": ${errMsg(err)}`);
				}
			}
		}
		if (created > 0) snackSuccess(t('notificationTracker.createdLinks').replace('{count}', String(created)));
		linkWarning = null;
		linkCreating = false;
		await doSave();
	}

	async function dismissLinkWarning() {
		linkWarning = null;
		await doSave();
	}

	async function toggle(tracker: Tracker) {
		if (toggling[tracker.id]) return;
		toggling = { ...toggling, [tracker.id]: true };
		try {
			await api(`/notification-trackers/${tracker.id}`, { method: 'PUT', body: JSON.stringify({ enabled: !tracker.enabled }) });
			await load();
			snackSuccess(tracker.enabled ? t('snack.trackerPaused') : t('snack.trackerResumed'));
		} catch (err: unknown) { snackError(errMsg(err)); } finally { toggling = { ...toggling, [tracker.id]: false }; }
	}

	function startDelete(tracker: Tracker) { confirmDelete = tracker; }

	async function doDelete() {
		if (!confirmDelete) return;
		try {
			await api(`/notification-trackers/${confirmDelete.id}`, { method: 'DELETE' });
			await load();
			snackSuccess(t('snack.trackerDeleted'));
		} catch (err: unknown) { const __m = errMsg(err); error = __m; snackError(__m); }
		confirmDelete = null;
	}

	function toggleCollection(collectionId: string) {
		form.collection_ids = form.collection_ids.includes(collectionId)
			? form.collection_ids.filter(id => id !== collectionId)
			: [...form.collection_ids, collectionId];
	}

	function formatDate(dateStr: string): string {
		if (!dateStr) return '';
		try {
			const d = parseDate(dateStr);
			return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' });
		} catch (e) { console.warn('Date format error:', e); return ''; }
	}

	// --- Linked Targets helpers ---
	function toggleExpand(trackerId: number) {
		if (expandedTracker === trackerId) { expandedTracker = null; return; }
		expandedTracker = trackerId;
	}

	function getProviderType(tracker: Tracker): string {
		const p = providers.find(p => p.id === tracker.provider_id);
		return p?.type || '';
	}

	function getProviderName(id: number): string {
		const p = providers.find(p => p.id === id);
		return p?.name || `#${id}`;
	}

	function getCollectionLabel(tracker: Tracker): string {
		const pt = getProviderType(tracker);
		const desc = getDescriptor(pt);
		return desc?.collectionMeta ? t(desc.collectionMeta.countLabel) : t('notificationTracker.collections_count');
	}

	/**
	 * Meta tiles for a tracker row. Visible on lg+ in the dead middle space
	 * between identity and actions. Mirrors the secondary text shown on narrow
	 * screens, but as live tiles users can scan at a glance.
	 */
	function trackerTiles(tracker: Tracker): MetaTile[] {
		const tiles: MetaTile[] = [];
		const trkDesc = getDescriptor(getProviderType(tracker));
		// Status — armed/paused with color tone
		tiles.push(tracker.enabled
			? { icon: 'mdiPulse', label: t('notificationTracker.armed'), tone: 'mint' }
			: { icon: 'mdiPauseCircleOutline', label: t('notificationTracker.paused'), tone: 'citrus' });
		// Provider
		tiles.push({
			icon: 'mdiServer',
			label: getProviderName(tracker.provider_id),
			tone: 'lavender',
		});
		// Collections — count + label (varies per provider descriptor)
		const collCount = (tracker.collection_ids || []).length;
		if (collCount > 0 || !trkDesc?.webhookBased) {
			tiles.push({
				icon: 'mdiFolderMultipleOutline',
				value: String(collCount),
				label: getCollectionLabel(tracker),
				tone: 'sky',
			});
		}
		// Scan interval — only meaningful for polling trackers
		if (!trkDesc?.webhookBased) {
			tiles.push({
				icon: 'mdiTimerOutline',
				value: `${tracker.scan_interval}s`,
				label: t('notificationTracker.every').trim(),
				tone: 'orchid',
			});
		}
		// Linked targets
		const tgtCount = (tracker.tracker_targets || []).length;
		tiles.push({
			icon: 'mdiTarget',
			value: String(tgtCount),
			label: t('notificationTracker.linkedTargets'),
			tone: tgtCount > 0 ? 'mint' : 'coral',
		});
		return tiles;
	}

	function configsForTracker(tracker: Tracker, configs: (TrackingConfig | TemplateConfig)[]): (TrackingConfig | TemplateConfig)[] {
		const pt = getProviderType(tracker);
		return pt ? configs.filter((c) => c.provider_type === pt) : configs;
	}

	function getUnlinkedTargets(tracker: Tracker): NotificationTarget[] {
		const linkedIds = new Set((tracker.tracker_targets || []).map((tt: TrackerTarget) => tt.target_id));
		return targets.filter(t => !linkedIds.has(t.id));
	}

	async function addTargetLink(trackerId: number) {
		const targetId = newLinkTargetId[trackerId];
		if (!targetId) return;
		addingTarget = { ...addingTarget, [trackerId]: true };
		try {
			await api(`/notification-trackers/${trackerId}/targets`, {
				method: 'POST',
				body: JSON.stringify({
					target_id: targetId,
					tracking_config_id: newLinkTrackingConfigId[trackerId] || null,
					template_config_id: newLinkTemplateConfigId[trackerId] || null,
				}),
			});
			newLinkTargetId[trackerId] = 0;
			newLinkTrackingConfigId[trackerId] = 0;
			newLinkTemplateConfigId[trackerId] = 0;
			await load();
			snackSuccess(t('snack.targetLinked'));
		} catch (err: unknown) { snackError(errMsg(err)); }
		addingTarget = { ...addingTarget, [trackerId]: false };
	}

	async function removeTargetLink(trackerId: number, ttId: number) {
		try {
			await api(`/notification-trackers/${trackerId}/targets/${ttId}`, { method: 'DELETE' });
			await load();
			snackSuccess(t('snack.targetUnlinked'));
		} catch (err: unknown) { snackError(errMsg(err)); }
	}

	async function updateTargetLink(trackerId: number, tt: TrackerTarget, field: string, value: string | number | boolean | null) {
		try {
			await api(`/notification-trackers/${trackerId}/targets/${tt.id}`, {
				method: 'PUT',
				body: JSON.stringify({ [field]: value }),
			});
			await load();
		} catch (err: unknown) { snackError(errMsg(err)); }
	}

	async function testTrackerTarget(trackerId: number, ttId: number, testType: string) {
		testMenuOpen = null;
		const key = `${ttId}_${testType}`;
		if (ttTesting[key]) return;
		ttTesting = { ...ttTesting, [key]: testType };
		try {
			// The endpoint returns 200 OK with ``{success: false, error: "..."}``
			// on soft failures (missing template slot, no matching assets,
			// provider unreachable, etc.), so checking for a thrown exception
			// is not enough.  Surface ``error`` as a snackError when present.
			const res = await api<{ success?: boolean; error?: string; target?: string }>(
				`/notification-trackers/${trackerId}/targets/${ttId}/test/${testType}?locale=${getLocale()}`,
				{ method: 'POST' },
			);
			if (res && res.success === false) {
				snackError(res.error || t('common.error'));
			} else {
				snackSuccess(t('snack.targetTestSent'));
			}
		} catch (err: unknown) {
			snackError(errMsg(err));
		} finally {
			ttTesting = { ...ttTesting, [key]: '' };
		}
	}

	function openTestMenu(ttId: number, event: MouseEvent) {
		const btn = event.currentTarget as HTMLElement;
		const rect = btn.getBoundingClientRect();
		testMenuStyle = `position:fixed; z-index:9999; top:${rect.bottom + 4}px; right:${window.innerWidth - rect.right}px;`;
		testMenuTrackerId = notificationTrackers.find(t => t.tracker_targets?.some((x: TrackerTarget) => String(x.id) === String(ttId)))?.id ?? null;
		testMenuOpen = testMenuOpen === String(ttId) ? null : String(ttId);
	}

	function handleTestFromMenu(ttId: number, testType: string) {
		const trackerId = notificationTrackers.find(t => t.tracker_targets?.some((x: TrackerTarget) => String(x.id) === String(ttId)))?.id;
		if (trackerId) testTrackerTarget(trackerId, ttId, testType);
	}
</script>

<PageHeader
	title={t('notificationTracker.title')}
	emphasis={t('notificationTracker.titleEmphasis')}
	description={t('notificationTracker.description')}
	crumb={t('crumbs.routingNotification')}
	count={notificationTrackers.length}
	countLabel={t('dashboard.trackersShort')}
	pills={headerPills}
>
	<Button size="sm" onclick={() => { showForm ? (showForm = false, editing = null) : openNew(); }}>
		{showForm ? t('notificationTracker.cancel') : t('notificationTracker.newTracker')}
	</Button>
</PageHeader>

{#if !loaded}
<Loading />
{:else if loadError}
	<Card>
		<ErrorBanner message={loadError} class="mb-0" />
	</Card>
{:else if showForm}
	<TrackerForm
		bind:form
		{providerItems}
		{collections}
		{users}
		bind:collectionFilter
		trackingConfigItems={trackingConfigs.filter(c => !selectedProviderType || c.provider_type === selectedProviderType).map(c => ({ value: c.id, label: c.name, icon: (c as any).icon || 'mdiCog' }))}
		templateConfigItems={templateConfigs.filter(c => !selectedProviderType || c.provider_type === selectedProviderType).map(c => ({ value: c.id, label: c.name, icon: (c as any).icon || 'mdiFileDocumentEdit' }))}
		{editing}
		{submitting}
		{linkCheckLoading}
		{error}
		providerType={selectedProviderType}
		onsave={save}
		ontoggleCollection={toggleCollection}
		{formatDate}
		onnameinput={() => nameManuallyEdited = true}
	/>
{/if}

{#if loaded && !loadError}

{#if !showForm && allNotificationTrackers.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)]" />
	{#if !globalProviderFilter.id}
	<div class="w-48">
		<EntitySelect items={[{value: 0, label: t('common.allProviders'), icon: 'mdiFilterOff'}, ...providerItems]} bind:value={filterProviderId} placeholder={t('common.allProviders')} />
	</div>
	{/if}
</div>
{/if}

{#if allNotificationTrackers.length === 0 && !showForm}
	<Card>
		<EmptyState icon="mdiRadar" message={t('notificationTracker.noTrackers')} />
	</Card>
{:else if notificationTrackers.length === 0 && !showForm}
	<Card>
		<EmptyState icon="mdiFilterOff" message={t('common.noFilterResults')} />
	</Card>
{:else if !showForm}
	<div class="list-stack stagger-children">
		{#each notificationTrackers as tracker (tracker.id)}
			{@const trkDesc = getDescriptor(getProviderType(tracker))}
			<Card hover entityId={tracker.id}>
				<div class="list-row">
					<div class="list-row__identity">
						<div class="flex items-center gap-2 min-w-0">
							<span style="color: var(--color-primary);" class="shrink-0"><MdiIcon name={tracker.icon || 'mdiRadar'} size={20} /></span>
							<p class="font-medium truncate">{tracker.name}</p>
							<span class="text-xs px-1.5 py-0.5 rounded shrink-0 {tracker.enabled ? 'bg-[var(--color-success-bg)] text-[var(--color-success-fg)]' : 'bg-[var(--color-muted)] text-[var(--color-muted-foreground)]'}">
								{tracker.enabled ? t('notificationTracker.active') : t('notificationTracker.paused')}
							</span>
						</div>
						<div class="list-row__secondary mt-0.5">
							<CrossLink href="/providers" icon="mdiServer" label={getProviderName(tracker.provider_id)} entityId={tracker.provider_id} />
							<p class="text-sm text-[var(--color-muted-foreground)]">
								{(tracker.collection_ids || []).length} {getCollectionLabel(tracker)} ·
								{#if !trkDesc?.webhookBased}{t('notificationTracker.every')} {tracker.scan_interval}s ·{/if}
								{(tracker.tracker_targets || []).length} {t('notificationTracker.linkedTargets')}
							</p>
						</div>
					</div>
					<MetaStrip tiles={trackerTiles(tracker)} />
					<div class="list-row__actions flex-wrap justify-end">
						<IconButton icon="mdiPencil" title={t('common.edit')} onclick={() => edit(tracker)} />
						<IconButton icon={tracker.enabled ? 'mdiPause' : 'mdiPlay'} title={tracker.enabled ? t('notificationTracker.pause') : t('notificationTracker.resume')} onclick={() => toggle(tracker)} disabled={toggling[tracker.id]} />
						<button onclick={() => toggleExpand(tracker.id)}
							class="text-xs text-[var(--color-muted-foreground)] hover:underline px-2 py-1">
							{t('notificationTracker.linkedTargets')} {expandedTracker === tracker.id ? '▲' : '▼'}
						</button>
						<IconButton icon="mdiDelete" title={t('notificationTracker.delete')} onclick={() => startDelete(tracker)} variant="danger" />
					</div>
				</div>

				{#if expandedTracker === tracker.id}
					<LinkedTargetsSection
						{tracker}
						{trackingConfigs}
						{templateConfigs}
						unlinkedTargets={targets}
						newLinkTargetId={newLinkTargetId[tracker.id] || 0}
						newLinkTrackingConfigId={newLinkTrackingConfigId[tracker.id] || 0}
						newLinkTemplateConfigId={newLinkTemplateConfigId[tracker.id] || 0}
						addingTarget={addingTarget[tracker.id] || false}
						{ttTesting}
						configsForTracker={(configs) => configsForTracker(tracker, configs)}
						onupdateLink={(tt, field, value) => updateTargetLink(tracker.id, tt, field, value)}
						onremoveLink={(ttId) => removeTargetLink(tracker.id, ttId)}
						onaddLink={() => addTargetLink(tracker.id)}
						onopenTestMenu={openTestMenu}
						onchangeNewTarget={(v) => newLinkTargetId = { ...newLinkTargetId, [tracker.id]: v }}
						onchangeNewTrackingConfig={(v) => newLinkTrackingConfigId = { ...newLinkTrackingConfigId, [tracker.id]: v }}
						onchangeNewTemplateConfig={(v) => newLinkTemplateConfigId = { ...newLinkTemplateConfigId, [tracker.id]: v }}
					/>
				{/if}
			</Card>
		{/each}
	</div>
{/if}
{/if}

<TestMenu
	{testMenuOpen}
	{testMenuStyle}
	{ttTesting}
	testTypes={testTypes}
	ontest={handleTestFromMenu}
	onclose={() => testMenuOpen = null}
/>

<SharedLinkModal
	{linkWarning}
	{linkCreating}
	onclose={() => { linkWarning = null; }}
	onautoCreate={autoCreateLinks}
	ondismiss={dismissLinkWarning}
	onupdate={(remaining) => {
		if (!linkWarning) return;
		if (remaining.length === 0) {
			linkWarning = null;
			doSave();
		} else {
			linkWarning = { ...linkWarning, albums: remaining };
		}
	}}
/>

<ConfirmModal
	open={!!confirmDelete}
	message={t('notificationTracker.confirmDelete')}
	onconfirm={doDelete}
	oncancel={() => confirmDelete = null}
/>
