<script lang="ts">
	import { onMount, getContext } from 'svelte';
	import { models } from '$lib/stores';
	import {
		getSummary,
		getModelAnalytics,
		getUserAnalytics,
		getDailyStats,
		getTokenUsage
	} from '$lib/apis/analytics';
	import { getGroups } from '$lib/apis/groups';
	import Spinner from '$lib/components/common/Spinner.svelte';
	import ChevronUp from '$lib/components/icons/ChevronUp.svelte';
	import ChevronDown from '$lib/components/icons/ChevronDown.svelte';
	import ChartLine from './ChartLine.svelte';
	import AnalyticsModelModal from './AnalyticsModelModal.svelte';
	import Tooltip from '$lib/components/common/Tooltip.svelte';
	import { WEBUI_API_BASE_URL } from '$lib/constants';
	import { formatNumber } from '$lib/utils';
	import { goto } from '$app/navigation';

	const i18n = getContext('i18n');

	// Time period - persist in localStorage
	let selectedPeriod =
		(typeof localStorage !== 'undefined' && localStorage.getItem('analyticsPeriod')) || '7d';
	$: periods = [
		{ value: '24h', label: $i18n.t('Last 24 hours') },
		{ value: '7d', label: $i18n.t('Last 7 days') },
		{ value: '30d', label: $i18n.t('Last 30 days') },
		{ value: '90d', label: $i18n.t('Last 90 days') },
		{ value: 'all', label: $i18n.t('All time') }
	];

	// User group filter
	let groups: Array<{ id: string; name: string }> = [];
	let selectedGroupId: string | null = null;

	const getDateRange = (period: string): { start: number | null; end: number | null } => {
		const now = Math.floor(Date.now() / 1000);
		const day = 86400;
		switch (period) {
			case '24h':
				return { start: now - day, end: now };
			case '7d':
				return { start: now - 7 * day, end: now };
			case '30d':
				return { start: now - 30 * day, end: now };
			case '90d':
				return { start: now - 90 * day, end: now };
			default:
				return { start: null, end: null };
		}
	};

	// Data
	let summary = { total_messages: 0, total_chats: 0, total_models: 0, total_users: 0 };
	let modelStats: Array<{ model_id: string; count: number; name?: string }> = [];
	let userStats: Array<{ user_id: string; name?: string; email?: string; count: number }> = [];
	let dailyStats: Array<{ date: string; models: Record<string, number> }> = [];
	let tokenStats: Record<
		string,
		{ input_tokens: number; output_tokens: number; total_tokens: number }
	> = {};
	let totalTokens = { input: 0, output: 0, total: 0 };

	let loading = true;

	// Selected model for drill-down
	let selectedModel: { id: string; name: string } | null = null;
	let showModelModal = false;

	// Sorting
	let modelOrderBy = 'count';
	let modelDirection: 'asc' | 'desc' = 'desc';
	let userOrderBy = 'count';
	let userDirection: 'asc' | 'desc' = 'desc';

	const toggleModelSort = (key: string) => {
		if (modelOrderBy === key) {
			modelDirection = modelDirection === 'asc' ? 'desc' : 'asc';
		} else {
			modelOrderBy = key;
			modelDirection = key === 'name' ? 'asc' : 'desc';
		}
	};

	const toggleUserSort = (key: string) => {
		if (userOrderBy === key) {
			userDirection = userDirection === 'asc' ? 'desc' : 'asc';
		} else {
			userOrderBy = key;
			userDirection = key === 'user_id' ? 'asc' : 'desc';
		}
	};

	const loadDashboard = async () => {
		loading = true;
		try {
			const { start, end } = getDateRange(selectedPeriod);
			const granularity = selectedPeriod === '24h' ? 'hourly' : 'daily';
			const [summaryRes, modelsRes, usersRes, dailyRes, tokensRes] = await Promise.all([
				getSummary(localStorage.token, start, end, selectedGroupId),
				getModelAnalytics(localStorage.token, start, end, selectedGroupId),
				getUserAnalytics(localStorage.token, start, end, 50, selectedGroupId),
				getDailyStats(localStorage.token, start, end, granularity, selectedGroupId),
				getTokenUsage(localStorage.token, start, end, selectedGroupId)
			]);

			summary = summaryRes ?? summary;

			const modelsMap = new Map($models.map((m) => [m.id, m.name || m.id]));
			modelStats = (modelsRes?.models ?? []).map((entry) => ({
				...entry,
				name: modelsMap.get(entry.model_id) || entry.model_id
			}));

			userStats = usersRes?.users ?? [];
			dailyStats = dailyRes?.data ?? [];

			// Process token data
			if (tokensRes) {
				tokenStats = {};
				for (const m of tokensRes.models) {
					tokenStats[m.model_id] = {
						input_tokens: m.input_tokens,
						output_tokens: m.output_tokens,
						total_tokens: m.total_tokens
					};
				}
				totalTokens = {
					input: tokensRes.total_input_tokens,
					output: tokensRes.total_output_tokens,
					total: tokensRes.total_tokens
				};
			}
		} catch (err) {
			console.error('Dashboard load failed:', err);
		}
		loading = false;
	};

	$: if (selectedPeriod || selectedGroupId !== undefined) {
		loadDashboard();
	}

	onMount(async () => {
		// Load groups for filter
		try {
			const res = await getGroups(localStorage.token);
			groups = res ?? [];
		} catch (e) {
			console.error('Failed to load groups:', e);
		}
	});

	$: sortedModels = [...modelStats].sort((a, b) => {
		if (modelOrderBy === 'name') {
			return modelDirection === 'asc' ? a.name.localeCompare(b.name) : b.name.localeCompare(a.name);
		}
		if (modelOrderBy === 'tokens') {
			const aTokens = tokenStats[a.model_id]?.total_tokens ?? 0;
			const bTokens = tokenStats[b.model_id]?.total_tokens ?? 0;
			return modelDirection === 'asc' ? aTokens - bTokens : bTokens - aTokens;
		}
		return modelDirection === 'asc' ? a.count - b.count : b.count - a.count;
	});

	$: sortedUsers = [...userStats].sort((a, b) => {
		if (userOrderBy === 'name') {
			const nameA = a.name || a.user_id;
			const nameB = b.name || b.user_id;
			return userDirection === 'asc' ? nameA.localeCompare(nameB) : nameB.localeCompare(nameA);
		}
		if (userOrderBy === 'tokens') {
			const aTokens = a.total_tokens ?? 0;
			const bTokens = b.total_tokens ?? 0;
			return userDirection === 'asc' ? aTokens - bTokens : bTokens - aTokens;
		}
		return userDirection === 'asc' ? a.count - b.count : b.count - a.count;
	});

	$: totalModelMessages = modelStats.reduce((sum, m) => sum + m.count, 0);

	// Persist period selection
	$: if (typeof localStorage !== 'undefined' && selectedPeriod) {
		localStorage.setItem('analyticsPeriod', selectedPeriod);
	}

	onMount(loadDashboard);
</script>

<!-- Header with title and period selector -->
<div
	class="pt-0.5 pb-1 gap-1 flex flex-row justify-between items-center sticky top-0 z-10 bg-white dark:bg-gray-900"
>
	<div class="text-lg font-medium px-0.5">
		{$i18n.t('Analytics')}
	</div>
	<div class="flex items-center gap-2">
		{#if groups.length > 0}
			<select
				bind:value={selectedGroupId}
				class="w-fit pr-8 rounded-sm px-2 text-xs bg-transparent outline-none text-right"
			>
				<option value={null}>{$i18n.t('All Users')}</option>
				{#each groups as group}
					<option value={group.id}>{group.name}</option>
				{/each}
			</select>
		{/if}
		<select
			bind:value={selectedPeriod}
			class="w-fit pr-8 rounded-sm px-2 text-xs bg-transparent outline-none text-right"
		>
			{#each periods as period}
				<option value={period.value}>{period.label}</option>
			{/each}
		</select>
	</div>
</div>

<!-- Model Details Modal -->
<AnalyticsModelModal
	bind:show={showModelModal}
	model={selectedModel}
	startDate={getDateRange(selectedPeriod).start}
	endDate={getDateRange(selectedPeriod).end}
/>

<!-- Summary stats -->
{#if !loading}
	<div class="flex gap-3 text-xs text-gray-500 dark:text-gray-400 px-0.5 pb-2">
		<span
			><span class="font-medium text-gray-900 dark:text-gray-300"
				>{summary.total_messages.toLocaleString()}</span
			>
			{$i18n.t('messages')}</span
		>
		<Tooltip content={$i18n.t('Token counts are estimates and may not reflect actual API usage')}>
			<span class="cursor-help"
				><span class="font-medium text-gray-900 dark:text-gray-300"
					>{formatNumber(totalTokens.total)}</span
				>
				{$i18n.t('tokens')}</span
			>
		</Tooltip>
		<span
			><span class="font-medium text-gray-900 dark:text-gray-300"
				>{summary.total_chats.toLocaleString()}</span
			>
			{$i18n.t('chats')}</span
		>
		<span
			><span class="font-medium text-gray-900 dark:text-gray-300">{summary.total_users}</span>
			{$i18n.t('users')}</span
		>
	</div>

	<!-- Daily usage chart -->
	{#if dailyStats.length > 1}
		{@const allModels = [...new Set(dailyStats.flatMap((d) => Object.keys(d.models || {})))]}
		{@const topModels = allModels.slice(0, 8)}
		{@const chartColors = [
			'#3b82f6',
			'#10b981',
			'#f59e0b',
			'#ef4444',
			'#8b5cf6',
			'#ec4899',
			'#06b6d4',
			'#84cc16'
		]}
		{@const periodMap = { '24h': 'hour', '7d': 'week', '30d': 'month', '90d': 'year', all: 'all' }}
		<div class="mb-4">
			<div class="text-xs font-medium text-gray-600 dark:text-gray-400 mb-2 px-0.5">
				{selectedPeriod === '24h' ? $i18n.t('Hourly Messages') : $i18n.t('Daily Messages')}
			</div>
			<ChartLine
				data={dailyStats}
				models={topModels}
				colors={chartColors}
				height={200}
				period={periodMap[selectedPeriod] || 'week'}
			/>
		</div>
	{/if}
{/if}

{#if loading}
	<div class="my-10 flex justify-center">
		<Spinner className="size-5" />
	</div>
{:else}
	<div class="grid md:grid-cols-2 gap-4">
		<!-- Model Usage Table -->
		<div>
			<div class="text-xs font-medium text-gray-700 dark:text-gray-300 mb-1 px-0.5">
				{$i18n.t('Model Usage')}
			</div>
			<div class="scrollbar-hidden relative whitespace-nowrap overflow-x-auto max-w-full">
				<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400 table-auto">
					<thead class="text-xs text-gray-800 uppercase bg-transparent dark:text-gray-200">
						<tr class="border-b-[1.5px] border-gray-50 dark:border-gray-850/30">
							<th scope="col" class="px-2.5 py-2 w-8">#</th>
							<th
								scope="col"
								class="px-2.5 py-2 cursor-pointer select-none"
								on:click={() => toggleModelSort('name')}
							>
								<div class="flex gap-1.5 items-center">
									{$i18n.t('Model')}
									{#if modelOrderBy === 'name'}
										<span class="font-normal">
											{#if modelDirection === 'asc'}<ChevronUp
													className="size-2"
												/>{:else}<ChevronDown className="size-2" />{/if}
										</span>
									{:else}
										<span class="invisible"><ChevronUp className="size-2" /></span>
									{/if}
								</div>
							</th>
							<th
								scope="col"
								class="px-2.5 py-2 cursor-pointer select-none text-right"
								on:click={() => toggleModelSort('count')}
							>
								<div class="flex gap-1.5 items-center justify-end">
									{$i18n.t('Messages')}
									{#if modelOrderBy === 'count'}
										<span class="font-normal">
											{#if modelDirection === 'asc'}<ChevronUp
													className="size-2"
												/>{:else}<ChevronDown className="size-2" />{/if}
										</span>
									{:else}
										<span class="invisible"><ChevronUp className="size-2" /></span>
									{/if}
								</div>
							</th>
							<th
								scope="col"
								class="px-2.5 py-2 cursor-pointer select-none text-right"
								on:click={() => toggleModelSort('tokens')}
							>
								<div class="flex gap-1.5 items-center justify-end">
									{$i18n.t('Tokens')}
									{#if modelOrderBy === 'tokens'}
										<span class="font-normal">
											{#if modelDirection === 'asc'}<ChevronUp
													className="size-2"
												/>{:else}<ChevronDown className="size-2" />{/if}
										</span>
									{:else}
										<span class="invisible"><ChevronUp className="size-2" /></span>
									{/if}
								</div>
							</th>
							<th
								scope="col"
								class="px-2.5 py-2 cursor-pointer select-none text-right w-16"
								on:click={() => toggleModelSort('percentage')}
							>
								<div class="flex gap-1.5 items-center justify-end">
									%
									{#if modelOrderBy === 'percentage'}
										<span class="font-normal">
											{#if modelDirection === 'asc'}<ChevronUp
													className="size-2"
												/>{:else}<ChevronDown className="size-2" />{/if}
										</span>
									{:else}
										<span class="invisible"><ChevronUp className="size-2" /></span>
									{/if}
								</div>
							</th>
						</tr>
					</thead>
					<tbody>
						{#each sortedModels as model, idx (model.model_id)}
							<tr
								class="bg-white dark:bg-gray-900 dark:border-gray-850 text-xs cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
								on:click={() => {
									selectedModel = { id: model.model_id, name: model.name };
									showModelModal = true;
								}}
							>
								<td class="px-3 py-1 text-gray-400">{idx + 1}</td>
								<td class="px-3 py-1 font-medium text-gray-900 dark:text-white">
									<div class="flex items-center gap-2">
										<img
											src="{WEBUI_API_BASE_URL}/models/model/profile/image?id={model.model_id}"
											alt={model.name}
											class="size-5 rounded-full object-cover shrink-0"
											on:error={(e) => {
												e.target.src = '/favicon.png';
											}}
										/>
										<span class="truncate max-w-[150px]">{model.name}</span>
									</div>
								</td>
								<td class="px-3 py-1 text-right">{model.count.toLocaleString()}</td>
								<td class="px-3 py-1 text-right"
									>{formatNumber(tokenStats[model.model_id]?.total_tokens ?? 0)}</td
								>
								<td class="px-3 py-1 text-right text-gray-400">
									{totalModelMessages > 0
										? ((model.count / totalModelMessages) * 100).toFixed(1)
										: 0}%
								</td>
							</tr>
						{/each}
						{#if sortedModels.length === 0}
							<tr
								><td colspan="5" class="px-3 py-2 text-center text-gray-400"
									>{$i18n.t('No data')}</td
								></tr
							>
						{/if}
					</tbody>
				</table>
			</div>
		</div>

		<!-- User Activity Table -->
		<div>
			<div class="text-xs font-medium text-gray-700 dark:text-gray-300 mb-1 px-0.5">
				{$i18n.t('User Activity')}
			</div>
			<div class="scrollbar-hidden relative whitespace-nowrap overflow-x-auto max-w-full">
				<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400 table-auto">
					<thead class="text-xs text-gray-800 uppercase bg-transparent dark:text-gray-200">
						<tr class="border-b-[1.5px] border-gray-50 dark:border-gray-850/30">
							<th scope="col" class="px-2.5 py-2 w-8">#</th>
							<th
								scope="col"
								class="px-2.5 py-2 cursor-pointer select-none"
								on:click={() => toggleUserSort('name')}
							>
								<div class="flex gap-1.5 items-center">
									{$i18n.t('User')}
									{#if userOrderBy === 'name'}
										<span class="font-normal">
											{#if userDirection === 'asc'}<ChevronUp
													className="size-2"
												/>{:else}<ChevronDown className="size-2" />{/if}
										</span>
									{:else}
										<span class="invisible"><ChevronUp className="size-2" /></span>
									{/if}
								</div>
							</th>
							<th
								scope="col"
								class="px-2.5 py-2 cursor-pointer select-none text-right"
								on:click={() => toggleUserSort('count')}
							>
								<div class="flex gap-1.5 items-center justify-end">
									{$i18n.t('Messages')}
									{#if userOrderBy === 'count'}
										<span class="font-normal">
											{#if userDirection === 'asc'}<ChevronUp
													className="size-2"
												/>{:else}<ChevronDown className="size-2" />{/if}
										</span>
									{:else}
										<span class="invisible"><ChevronUp className="size-2" /></span>
									{/if}
								</div>
							</th>
							<th
								scope="col"
								class="px-2.5 py-2 cursor-pointer select-none text-right"
								on:click={() => toggleUserSort('tokens')}
							>
								<div class="flex gap-1.5 items-center justify-end">
									{$i18n.t('Tokens')}
									{#if userOrderBy === 'tokens'}
										<span class="font-normal">
											{#if userDirection === 'asc'}<ChevronUp
													className="size-2"
												/>{:else}<ChevronDown className="size-2" />{/if}
										</span>
									{:else}
										<span class="invisible"><ChevronUp className="size-2" /></span>
									{/if}
								</div>
							</th>
						</tr>
					</thead>
					<tbody>
						{#each sortedUsers as user, idx (user.user_id)}
							<tr class="bg-white dark:bg-gray-900 dark:border-gray-850 text-xs">
								<td class="px-3 py-1 text-gray-400">{idx + 1}</td>
								<td class="px-3 py-1 font-medium text-gray-900 dark:text-white">
									<div class="flex items-center gap-2">
										<img
											src="{WEBUI_API_BASE_URL}/users/{user.user_id}/profile/image"
											alt={user.name || 'User'}
											class="size-5 rounded-full object-cover shrink-0"
											on:error={(e) => {
												e.target.src = '/user.png';
											}}
										/>
										<span class="truncate max-w-[150px]"
											>{user.name || user.email || user.user_id.substring(0, 8)}</span
										>
									</div>
								</td>
								<td class="px-3 py-1 text-right">{user.count.toLocaleString()}</td>
								<td class="px-3 py-1 text-right">{formatNumber(user.total_tokens ?? 0)}</td>
							</tr>
						{/each}
						{#if sortedUsers.length === 0}
							<tr
								><td colspan="4" class="px-3 py-2 text-center text-gray-400"
									>{$i18n.t('No data')}</td
								></tr
							>
						{/if}
					</tbody>
				</table>
			</div>
		</div>
	</div>

	<div class="text-gray-500 text-xs mt-1.5 text-right">
		ⓘ {$i18n.t('Message counts are based on assistant responses.')}
	</div>
{/if}
