<script lang="ts">
	import { Card, Badge, Button, Spinner } from '$lib/components';
	import { data } from '$lib/stores/data.svelte';
	import { pingUrl } from '$lib/api/admin';
	import { RefreshCw, ChevronDown, ChevronRight, Globe, CheckCircle, XCircle, Loader } from 'lucide-svelte';
	import { highlightCaddyfile } from '$lib/highlight';
	import { onMount } from 'svelte';

	let caddyLoading = $state(false);
	let expandedTenants = $state<Set<string>>(new Set());
	let systemConfigExpanded = $state(true);

	// Health check state: url -> { status, ok, checking }
	type HealthState = { status: number; ok: boolean; checking: boolean; error?: string };
	let health = $state<Record<string, HealthState>>({});

	async function handleRefreshCaddy() {
		caddyLoading = true;
		try {
			await data.refetchCaddy();
			runHealthChecks();
		} finally {
			caddyLoading = false;
		}
	}

	function toggleTenant(name: string) {
		const next = new Set(expandedTenants);
		if (next.has(name)) next.delete(name);
		else next.add(name);
		expandedTenants = next;
	}

	// Extract domains from a Caddyfile content string
	function extractDomains(content: string): string[] {
		const domains: string[] = [];
		// Match lines like: domain1.example.com, domain2.example.com {
		const re = /^([^#\n][^{\n]*\.devitltd\.uk[^{\n]*)\s*\{/gm;
		let m;
		while ((m = re.exec(content)) !== null) {
			for (const part of m[1].split(',')) {
				const d = part.trim();
				if (d && d.includes('.devitltd.uk') && !d.startsWith('*') && d.split('.devitltd.uk')[0].includes('.')) {
					domains.push(d);
				}
			}
		}
		return [...new Set(domains)];
	}

	// Derived: list of { domain, tenantId } across all tenant files
	let apps = $derived.by(() => {
		if (!data.caddyConfig) return [];
		const result: { domain: string; tenantId: string }[] = [];
		for (const file of data.caddyConfig.tenantFiles) {
			for (const domain of extractDomains(file.content)) {
				result.push({ domain, tenantId: file.tenantId });
			}
		}
		return result.sort((a, b) => a.tenantId.localeCompare(b.tenantId) || a.domain.localeCompare(b.domain));
	});

	let uniqueTenantCount = $derived(
		new Set(apps.map(a => a.tenantId)).size
	);

	async function checkOne(domain: string) {
		const url = 'https://' + domain;
		health[domain] = { status: 0, ok: false, checking: true };
		try {
			const result = await pingUrl(url);
			health[domain] = { status: result.status, ok: result.ok, checking: false, error: result.error };
		} catch (e: any) {
			health[domain] = { status: 0, ok: false, checking: false, error: e.message };
		}
	}

	function runHealthChecks() {
		for (const app of apps) {
			checkOne(app.domain);
		}
	}

	onMount(() => {
		if (data.initialLoaded && apps.length > 0) runHealthChecks();
	});

	// Trigger health checks once apps are available
	let checksStarted = false;
	$effect(() => {
		if (!checksStarted && apps.length > 0) {
			checksStarted = true;
			runHealthChecks();
		}
	});

	function statusBadge(domain: string): { color: 'emerald' | 'rose' | 'amber' | 'dim'; label: string } {
		const h = health[domain];
		if (!h) return { color: 'dim', label: '—' };
		if (h.checking) return { color: 'amber', label: '...' };
		if (h.ok) return { color: 'emerald', label: String(h.status) };
		if (h.status > 0) return { color: 'rose', label: String(h.status) };
		return { color: 'rose', label: 'ERR' };
	}

	let allChecked = $derived(apps.length > 0 && apps.every(a => health[a.domain] && !health[a.domain].checking));
	let healthyCount = $derived(apps.filter(a => health[a.domain]?.ok).length);
	let unhealthyCount = $derived(apps.filter(a => health[a.domain] && !health[a.domain].checking && !health[a.domain].ok).length);
</script>

<div class="page">
	{#if !data.initialLoaded}
		<div class="center"><Spinner /></div>
	{:else}

		<!-- Stats row -->
		<div class="stats-row">
			<div class="stat-card">
				<Globe size={16} class="stat-icon" />
				<div class="stat-body">
					<span class="stat-value">{apps.length}</span>
					<span class="stat-label">Apps</span>
				</div>
			</div>
			<div class="stat-card">
				<div class="stat-body">
					<span class="stat-value">{uniqueTenantCount}</span>
					<span class="stat-label">Tenants with apps</span>
				</div>
			</div>
			{#if allChecked}
				<div class="stat-card">
					<CheckCircle size={16} class="stat-icon ok" />
					<div class="stat-body">
						<span class="stat-value">{healthyCount}</span>
						<span class="stat-label">Healthy</span>
					</div>
				</div>
				{#if unhealthyCount > 0}
					<div class="stat-card">
						<XCircle size={16} class="stat-icon err" />
						<div class="stat-body">
							<span class="stat-value">{unhealthyCount}</span>
							<span class="stat-label">Unhealthy</span>
						</div>
					</div>
				{/if}
			{:else if apps.length > 0}
				<div class="stat-card checking">
					<Loader size={14} class="spinning" />
					<span class="stat-label">Checking health…</span>
				</div>
			{/if}
		</div>

		<!-- Apps health table -->
		{#if apps.length > 0}
			<Card title="App Health">
				{#snippet actions()}
					<Button size="sm" variant="ghost" onclick={runHealthChecks}>
						<RefreshCw size={13} />
						Re-check
					</Button>
				{/snippet}
				<div class="apps-table-wrap">
					<table class="apps-table">
						<thead>
							<tr>
								<th>Domain</th>
								<th>Tenant</th>
								<th>Status</th>
							</tr>
						</thead>
						<tbody>
							{#each apps as app (app.domain)}
								{@const badge = statusBadge(app.domain)}
								<tr>
									<td>
										<a href="https://{app.domain}" target="_blank" rel="noopener" class="domain-link mono">
											{app.domain}
										</a>
									</td>
									<td class="mono dim">{app.tenantId}</td>
									<td>
										{#if health[app.domain]?.checking}
											<span class="checking-dots"><Loader size={12} class="spinning" /></span>
										{:else}
											<Badge color={badge.color}>{badge.label}</Badge>
										{/if}
									</td>
								</tr>
							{/each}
						</tbody>
					</table>
				</div>
			</Card>
		{/if}

		<!-- Caddy config card -->
		<Card title="Caddy Configuration">
			{#snippet actions()}
				<Button size="sm" variant="ghost" loading={caddyLoading} onclick={handleRefreshCaddy}>
					<RefreshCw size={13} />
					Refresh
				</Button>
			{/snippet}
			{#if data.caddyConfig}
				<!-- svelte-ignore a11y_click_events_have_key_events -->
				<!-- svelte-ignore a11y_no_static_element_interactions -->
				<div class="caddy-section">
					<div class="caddy-header" onclick={() => systemConfigExpanded = !systemConfigExpanded}>
						{#if systemConfigExpanded}<ChevronDown size={14} />{:else}<ChevronRight size={14} />{/if}
						<span class="caddy-label">System Config</span>
					</div>
					{#if systemConfigExpanded}
						<pre class="caddy-content">{@html highlightCaddyfile(data.caddyConfig.systemConfig)}</pre>
					{/if}
				</div>
				{#each data.caddyConfig.tenantFiles as file}
					{@const tenantApps = apps.filter(a => a.tenantId === file.tenantId)}
					<!-- svelte-ignore a11y_click_events_have_key_events -->
					<!-- svelte-ignore a11y_no_static_element_interactions -->
					<div class="caddy-section">
						<div class="caddy-header" onclick={() => toggleTenant(file.tenantId ?? file.name)}>
							{#if expandedTenants.has(file.tenantId ?? file.name)}<ChevronDown size={14} />{:else}<ChevronRight size={14} />{/if}
							<span class="caddy-label">{file.tenantId ?? file.name}</span>
							{#if tenantApps.length > 0}
								<span class="tenant-app-count">{tenantApps.length} app{tenantApps.length === 1 ? '' : 's'}</span>
							{/if}
						</div>
						{#if expandedTenants.has(file.tenantId ?? file.name)}
							<pre class="caddy-content">{@html highlightCaddyfile(file.content)}</pre>
						{/if}
					</div>
				{/each}
			{:else}
				<p class="no-data">No Caddy configuration loaded</p>
			{/if}
		</Card>
	{/if}
</div>

<style>
	.page {
		display: flex;
		flex-direction: column;
		gap: 20px;
	}
	.center {
		display: flex;
		justify-content: center;
		padding: 60px 0;
	}

	/* Stats */
	.stats-row {
		display: flex;
		gap: 12px;
		flex-wrap: wrap;
	}
	.stat-card {
		display: flex;
		align-items: center;
		gap: 10px;
		background: var(--surface-0);
		border: 1px solid var(--border-subtle);
		border-radius: var(--radius-lg);
		padding: 14px 18px;
		flex: 1;
		min-width: 120px;
	}
	.stat-card.checking {
		gap: 8px;
		color: var(--text-muted);
		font-size: var(--font-size-sm);
	}
	.stat-body {
		display: flex;
		flex-direction: column;
		gap: 2px;
	}
	.stat-value {
		font-size: var(--font-size-xl);
		font-weight: 700;
		color: var(--text-bright);
		font-family: var(--font-mono);
		line-height: 1;
	}
	.stat-label {
		font-size: var(--font-size-xs);
		color: var(--text-muted);
		text-transform: uppercase;
		letter-spacing: 0.05em;
	}
	.stat-card :global(.stat-icon) { color: var(--text-muted); flex-shrink: 0; }
	.stat-card :global(.stat-icon.ok) { color: var(--emerald); }
	.stat-card :global(.stat-icon.err) { color: var(--rose); }

	/* Spinning animation for loader icon */
	:global(.spinning) {
		animation: spin 0.9s linear infinite;
		color: var(--text-muted);
	}
	@keyframes spin { to { transform: rotate(360deg); } }

	/* Apps table */
	.apps-table-wrap { overflow-x: auto; }
	.apps-table {
		width: 100%;
		border-collapse: collapse;
		font-size: var(--font-size-sm);
	}
	.apps-table th {
		text-align: left;
		padding: 7px 12px;
		font-size: var(--font-size-xs);
		font-weight: 600;
		text-transform: uppercase;
		letter-spacing: 0.05em;
		color: var(--text-muted);
		border-bottom: 1px solid var(--border-subtle);
	}
	.apps-table td {
		padding: 8px 12px;
		border-bottom: 1px solid var(--border-subtle);
		color: var(--text);
		vertical-align: middle;
	}
	.apps-table tbody tr:last-child td { border-bottom: none; }
	.apps-table tbody tr:hover td { background: var(--surface-hover, var(--surface-1)); }
	.domain-link {
		color: var(--text-bright);
		text-decoration: none;
		font-size: var(--font-size-xs);
	}
	.domain-link:hover { color: var(--accent); text-decoration: underline; }
	.checking-dots {
		display: inline-flex;
		align-items: center;
	}
	.mono { font-family: var(--font-mono); }
	.dim { color: var(--text-dim); }

	/* Caddy config */
	.caddy-section {
		border-bottom: 1px solid var(--border-subtle);
	}
	.caddy-section:last-child { border-bottom: none; }
	.caddy-header {
		display: flex;
		align-items: center;
		gap: 8px;
		padding: 10px 0;
		cursor: pointer;
		color: var(--text-dim);
		transition: color var(--duration) var(--ease);
	}
	.caddy-header:hover { color: var(--text-bright); }
	.caddy-label {
		font-size: var(--font-size-sm);
		font-weight: 500;
		font-family: var(--font-mono);
	}
	.tenant-app-count {
		margin-left: auto;
		font-size: var(--font-size-xs);
		color: var(--text-muted);
		font-family: var(--font-mono);
		background: var(--surface-2);
		padding: 1px 6px;
		border-radius: 99px;
	}
	.caddy-content {
		background: var(--surface-0);
		border: 1px solid var(--border);
		border-radius: var(--radius);
		padding: 10px 12px;
		font-family: var(--font-mono);
		font-size: 12px;
		line-height: 1.5;
		color: var(--text-dim);
		max-height: 300px;
		overflow: auto;
		white-space: pre;
		margin: 0 0 10px 0;
		max-width: 100%;
	}
	@media (max-width: 768px) {
		.caddy-content { font-size: 11px; padding: 8px 10px; white-space: pre-wrap; word-break: break-all; }
		.stats-row { gap: 8px; }
	}
	.no-data { color: var(--text-muted); font-size: var(--font-size-sm); }

	/* Caddyfile tokens */
	.caddy-content :global(.hl-comment) { color: var(--text-muted); font-style: italic; }
	.caddy-content :global(.hl-domain) { color: var(--emerald); font-weight: 600; }
	.caddy-content :global(.hl-directive) { color: var(--cyan); }
	.caddy-content :global(.hl-brace) { color: var(--text-dim); }
	.caddy-content :global(.hl-str) { color: var(--amber); }
	.caddy-content :global(.hl-url) { color: var(--accent); }
	.caddy-content :global(.hl-path) { color: var(--text); text-decoration: underline; text-decoration-color: var(--border-strong); text-underline-offset: 2px; }
	.caddy-content :global(.hl-placeholder) { color: var(--amber); }
</style>
