<script lang="ts">
	import { Card, Badge, Spinner, Button, DataTable, Modal, FormField } from '$lib/components';
	import { createCredential, deleteCredential, reloginCredential, callbackCredential, importCredentialAuth, importPiHostAuth, startCodexDeviceAuth, finishCodexDeviceAuth, pollCodexDeviceStatus, startPiLogin, pollPiLoginStatus, submitPiLoginCode, getCredentialUsageLatest, snapshotCredentialUsage, getPiModels } from '$lib/api/admin';
	import { toast } from '$lib/stores/toast.svelte';
	import { data } from '$lib/stores/data.svelte';
	import type { Credential } from '$lib/types';
	import { Plus, RefreshCw, Trash2, LogIn } from 'lucide-svelte';
	import { navbarExtra } from '$lib/stores/navbar-extra.svelte';
	import { onDestroy } from 'svelte';

	// Add modal
	let showAdd = $state(false);
	let addForm = $state({
		id: '',
		provider: 'claude' as 'claude' | 'codex' | 'pi',
		pi_backend: '',
		type: 'oauth' as 'oauth' | 'apikey',
		label: '',
		plan_type: '',
		api_key: '',
	});
	let piProviders = $state<{ id: string; label: string; modelCount: number }[]>([]);
	let loadingPiProviders = $state(false);
	let adding = $state(false);
	let authorizeUrl = $state('');

	$effect(() => {
		if (addForm.provider === 'pi' && piProviders.length === 0 && !loadingPiProviders) {
			loadingPiProviders = true;
			getPiModels().then(r => { piProviders = r.providers; loadingPiProviders = false; }).catch(() => { loadingPiProviders = false; });
		}
	});

	// Delete confirmation
	let deleteTarget = $state<string | null>(null);
	let deleting = $state(false);

	// Re-auth modal state
	let loginTarget = $state<Credential | null>(null);
	let loggingIn = $state(false);
	let loginAuthorizeUrl = $state('');
	let loginAuthorizeState = $state('');
	let callbackCode = $state('');
	let importAuthJson = $state('');
	let codexDeviceUrl = $state('');
	let codexUserCode = $state('');
	let codexDevicePending = $state(false);

	let codexPollTimer = $state<ReturnType<typeof setInterval> | null>(null);

	// Pi OAuth login state
	let piLoginUrl = $state('');
	let piLoginProgress = $state<string[]>([]);
	let piLoginPollTimer = $state<ReturnType<typeof setInterval> | null>(null);
	let piManualCode = $state('');
	let piNeedsManualCode = $state(false);
	let piLoginInstructions = $state('');
	let piSubmittingCode = $state(false);

	let usageLoading = $state(false);
	let usageSnapshots = $state<Record<string, any>>({});

	function statusColor(status: string): 'emerald' | 'rose' | 'amber' | 'dim' {
		if (status === 'active') return 'emerald';
		if (status === 'error') return 'rose';
		if (status === 'expired') return 'amber';
		return 'dim';
	}

	function formatDate(d?: string): string {
		if (!d) return '-';
		return new Date(d).toLocaleString();
	}

	function formatCompactDateTime24(d?: Date): string {
		if (!d) return '-';
		if (Number.isNaN(d.getTime())) return '-';
		const dayMonth = new Intl.DateTimeFormat('en-GB', { day: 'numeric', month: 'short' }).format(d);
		const hm = new Intl.DateTimeFormat('en-GB', { hour: '2-digit', minute: '2-digit', hour12: false }).format(d);
		return `${dayMonth} ${hm}`;
	}

	function formatCompactDateTime12(d?: Date): string {
		if (!d) return '-';
		if (Number.isNaN(d.getTime())) return '-';
		const parts = new Intl.DateTimeFormat('en-GB', {
			day: 'numeric',
			month: 'short',
			hour: 'numeric',
			minute: '2-digit',
			hour12: true
		}).formatToParts(d);
		const day = parts.find((p) => p.type === 'day')?.value ?? '';
		const month = parts.find((p) => p.type === 'month')?.value ?? '';
		const hour = parts.find((p) => p.type === 'hour')?.value ?? '';
		const minute = parts.find((p) => p.type === 'minute')?.value ?? '';
		const dayPeriod = (parts.find((p) => p.type === 'dayPeriod')?.value ?? '').toLowerCase();
		const time = minute === '00' ? `${hour}${dayPeriod}` : `${hour}:${minute}${dayPeriod}`;
		return `${day} ${month} ${time}`.trim();
	}

	function formatUnixSeconds(secs?: string): string {
		if (!secs) return '-';
		const n = Number(secs);
		if (!Number.isFinite(n)) return String(secs);
		// usage headers are unix seconds
		const d = new Date(n * 1000);
		if (Number.isNaN(d.getTime())) return String(secs);
		return formatCompactDateTime12(d);
	}

	function formatCodexReset(s?: string): string {
		if (!s) return '-';
		const m = /^(\d{1,2}):(\d{2}) on (\d{1,2}) ([A-Za-z]{3})$/.exec(s.trim());
		if (!m) return s;
		const hour = Number(m[1]);
		const minute = m[2];
		const day = m[3];
		const month = m[4];
		const ampm = hour >= 12 ? 'pm' : 'am';
		const h12 = ((hour + 11) % 12) + 1;
		const time = minute === '00' ? `${h12}${ampm}` : `${h12}:${minute}${ampm}`;
		return `${day} ${month} ${time}`;
	}

	function pctUsed(util?: string): string {
		if (!util) return '-';
		const n = Number(util);
		if (!Number.isFinite(n)) return String(util);
		return `${Math.round(n * 100)}% used`;
	}

	function pctNum(util?: string): number {
		const n = Number(util);
		if (!Number.isFinite(n)) return 0;
		return Math.max(0, Math.min(100, Math.round(n * 100)));
	}

	function pctLeftNum(leftPct?: string): number {
		const n = Number(leftPct);
		if (!Number.isFinite(n)) return 0;
		return Math.max(0, Math.min(100, Math.round(n)));
	}

	function pctUsedFromLeft(leftPct?: string): number {
		return 100 - pctLeftNum(leftPct);
	}

	async function refetchUsageSnapshots() {
		usageLoading = true;
		try {
			const result = await getCredentialUsageLatest();
			const map: Record<string, any> = {};
			for (const s of result.snapshots || []) map[s.credential_id] = s;
			usageSnapshots = map;
		} catch (e: any) {
			toast.error(e.message || 'Failed to load credential usage');
			usageSnapshots = {};
		} finally {
			usageLoading = false;
		}
	}

	async function captureUsageNow() {
		usageLoading = true;
		try {
			await snapshotCredentialUsage();
			await refetchUsageSnapshots();
			toast.success('Usage snapshot captured');
		} catch (e: any) {
			toast.error(e.message || 'Failed to capture usage snapshot');
		} finally {
			usageLoading = false;
		}
	}

	async function handleAdd() {
		adding = true;
		authorizeUrl = '';
		try {
			const payload: any = {
				id: addForm.id,
				provider: addForm.provider,
				pi_backend: addForm.provider === 'pi' ? (addForm.pi_backend || undefined) : undefined,
				type: addForm.type,
				label: addForm.label || undefined,
				plan_type: addForm.plan_type || undefined
			};
			if (addForm.type === 'apikey') {
				payload.api_key = addForm.api_key;
			}
			const result = await createCredential(payload);
			toast.success(result.message || 'Credential created');
			if (result.authorize_url) {
				window.open(result.authorize_url, '_blank', 'noopener');
				toast.info('Authorization opened in a new tab.');
			}
			await data.refetchCredentials();
			showAdd = false;
			resetAddForm();
			// For pi+oauth with a specific backend, auto-open the OAuth login flow
			if (payload.provider === 'pi' && payload.type === 'oauth' && payload.pi_backend) {
				const newCred = { id: payload.id, provider: 'pi', pi_backend: payload.pi_backend, type: 'oauth' } as any;
				await openLogin(newCred);
			}
		} catch (e: any) {
			toast.error(e.message || 'Failed to create credential');
		} finally {
			adding = false;
		}
	}

	async function handleDelete() {
		if (!deleteTarget) return;
		deleting = true;
		try {
			await deleteCredential(deleteTarget);
			await data.refetchCredentials();
			toast.success('Credential deleted');
			deleteTarget = null;
		} catch (e: any) {
			toast.error(e.message || 'Failed to delete credential');
		} finally {
			deleting = false;
		}
	}

	function stopCodexPoll() {
		if (codexPollTimer !== null) {
			clearInterval(codexPollTimer);
			codexPollTimer = null;
		}
	}

	async function submitPiCode() {
		if (!loginTarget || !piManualCode.trim()) return;
		piSubmittingCode = true;
		try {
			await submitPiLoginCode(loginTarget.id, piManualCode.trim());
			piManualCode = '';
			piNeedsManualCode = false;
			piLoginInstructions = '';
			toast.info('Code submitted, completing authentication…');
		} catch (e: any) {
			toast.error(e.message || 'Failed to submit code');
		} finally {
			piSubmittingCode = false;
		}
	}

	function stopPiPoll() {
		if (piLoginPollTimer) { clearInterval(piLoginPollTimer); piLoginPollTimer = null; }
	}

	function startPiPoll(credId: string) {
		stopPiPoll();
		piLoginPollTimer = setInterval(async () => {
			try {
				const result = await pollPiLoginStatus(credId);
				if (result.progress) piLoginProgress = result.progress;
				if (result.needsManualCode !== undefined) piNeedsManualCode = result.needsManualCode;
				if (result.instructions) piLoginInstructions = result.instructions;
				if (result.status === 'complete') {
					stopPiPoll();
					toast.success('Pi credential authenticated');
					loginTarget = null;
					data.refetchCredentials().catch(() => {});
					refetchUsageSnapshots().catch(() => {});
				} else if (result.status === 'error') {
					stopPiPoll();
					toast.error(result.error || 'Pi login failed');
				}
			} catch {
				// ignore poll errors
			}
		}, 2000);
	}

	function startCodexPoll(credId: string) {
		stopCodexPoll();
		codexPollTimer = setInterval(async () => {
			try {
				const result = await pollCodexDeviceStatus(credId);
				if (result.status === 'ok') {
					stopCodexPoll();
					toast.success('Codex credential re-authenticated');
					loginTarget = null;
					codexDevicePending = false;
					data.refetchCredentials().catch(() => {});
					refetchUsageSnapshots().catch(() => {});
				}
			} catch {
				// ignore poll errors silently
			}
		}, 3000);
	}

	async function openLogin(cred: Credential) {
		loginTarget = cred;
		loggingIn = true;
		loginAuthorizeUrl = '';
		loginAuthorizeState = '';
		callbackCode = '';
		importAuthJson = '';
		codexDeviceUrl = '';
		codexUserCode = '';
		codexDevicePending = false;
		stopCodexPoll();
		try {
			const provider = cred.provider ?? 'claude';
			if (cred.type !== 'oauth') {
				toast.info('Only OAuth credentials support login');
				return;
			}
			if (provider === 'claude') {
				const result = await reloginCredential(cred.id);
				loginAuthorizeUrl = result.authorize_url;
				loginAuthorizeState = result.state;
				window.open(loginAuthorizeUrl, '_blank', 'noopener');
				toast.info('Authorization opened in a new tab. Paste the callback code to finish.');
			} else if (provider === 'codex') {
				const result = await startCodexDeviceAuth(cred.id);
				codexDeviceUrl = result.authorize_url;
				codexUserCode = result.user_code;
				window.open(codexDeviceUrl, '_blank', 'noopener');
				toast.info('Codex authorization opened. Enter the code shown, then sign in.');
				startCodexPoll(cred.id);
			} else if (provider === 'pi') {
				piLoginUrl = '';
				piLoginProgress = [];
				piManualCode = '';
				piNeedsManualCode = false;
			piLoginInstructions = '';
				const result = await startPiLogin(cred.id);
				piLoginUrl = result.authorize_url;
				if (result.needsManualCode) piNeedsManualCode = true;
				if (result.instructions) piLoginInstructions = result.instructions;
				window.open(piLoginUrl, '_blank', 'noopener');
				toast.info('Pi OAuth opened in a new tab. Complete sign-in there.');
				startPiPoll(cred.id);
			} else {
				toast.error('Unsupported provider');
			}
		} catch (e: any) {
			toast.error(e.message || 'Failed to start login');
			// Don't close modal on start failure — let user see the error and retry
		} finally {
			loggingIn = false;
		}
	}

	async function completeClaudeLogin() {
		if (!loginTarget) return;
		if (!callbackCode.trim()) {
			toast.error('Paste the callback code');
			return;
		}
		loggingIn = true;
		try {
			const result = await callbackCredential(loginTarget.id, { code: callbackCode.trim(), state: loginAuthorizeState });
			toast.success(result?.message || 'Credential re-authenticated');
			loginTarget = null;
			data.refetchCredentials().catch(() => {});
			refetchUsageSnapshots().catch(() => {});
		} catch (e: any) {
			toast.error(e.message || 'Failed to finish login');
		} finally {
			loggingIn = false;
		}
	}

	async function importPiLogin() {
		if (!loginTarget) return;
		loggingIn = true;
		try {
			await importPiHostAuth(loginTarget.id);
			toast.success('Pi auth.json imported from host');
			loginTarget = null;
			await data.refetch();
		} catch (e: any) {
			toast.error(e.message || 'Failed to import pi auth');
		} finally {
			loggingIn = false;
		}
	}

	async function completeCodexImport() {
		if (!loginTarget) return;
		loggingIn = true;
		try {
			const result = await finishCodexDeviceAuth(loginTarget.id);
			if (result.status === 'pending') {
				toast.info(result.message || 'Still waiting for Codex to complete sign-in…');
				// Keep polling, don't disable the button
				return;
			}
			stopCodexPoll();
			toast.success(result?.message || 'Credential re-authenticated');
			loginTarget = null;
			codexDevicePending = false;
			data.refetchCredentials().catch(() => {});
			refetchUsageSnapshots().catch(() => {});
		} catch (e: any) {
			toast.error(e.message || 'Failed to import auth bundle');
		} finally {
			loggingIn = false;
		}
	}

	function resetAddForm() {
		addForm = { id: '', provider: 'claude', pi_backend: '', type: 'oauth', label: '', plan_type: '', api_key: '' };
		authorizeUrl = '';
	}

	function openAdd() {
		resetAddForm();
		showAdd = true;
	}

	$effect(() => {
		navbarExtra.set(addCredBtn);
		return () => navbarExtra.set(null);
	});
	onDestroy(() => navbarExtra.set(null));

	$effect(() => {
		// Load snapshots once credentials have been fetched.
		if (data.initialLoaded) refetchUsageSnapshots();
	});
</script>

{#snippet addCredBtn()}
	<Button variant="primary" size="sm" onclick={openAdd}>
		<Plus size={14} />
		Add Credential
	</Button>
{/snippet}

<div class="page">

	{#if !data.initialLoaded}
		<div class="center"><Spinner /></div>
	{:else}
		<Card>
			<div class="toolbar">
				<Button size="sm" variant="ghost" loading={usageLoading} onclick={refetchUsageSnapshots}>
					<RefreshCw size={13} />
					Refresh Usage
				</Button>
				<Button size="sm" variant="secondary" loading={usageLoading} onclick={captureUsageNow}>
					Capture Now
				</Button>
			</div>
			<DataTable data={data.credentials} empty="No credentials configured">
				{#snippet mobileRow(cred: Credential)}
					<div class="mobile-cred-card">
						<div class="mobile-cred-top">
							<span class="mobile-cred-id mono">{cred.id}</span>
							<div class="mobile-cred-badges">
								<Badge color={statusColor(cred.status)}>{cred.status}</Badge>
								<Badge color={cred.provider === 'codex' ? 'accent' : cred.provider === 'pi' ? 'violet' : 'cyan'}>{cred.provider ?? 'claude'}{cred.pi_backend ? ' / ' + cred.pi_backend : ''}</Badge>
								<Badge color={cred.type === 'oauth' ? 'cyan' : 'accent'}>{cred.type}</Badge>
							</div>
						</div>
						{#if cred.label}
							<div class="mobile-cred-label">{cred.label}</div>
						{/if}
						{#if cred.plan_type}
							<div class="mobile-cred-meta">
								<span class="mobile-meta-key">Plan</span>
								<Badge color="dim">{cred.plan_type}</Badge>
							</div>
						{/if}
						{#if cred.expires_at}
							<div class="mobile-cred-meta">
								<span class="mobile-meta-key">Expires</span>
								<span class="mobile-meta-val">{formatDate(cred.expires_at)}</span>
							</div>
						{/if}
						<div class="mobile-cred-meta">
							<span class="mobile-meta-key">Requests</span>
							<span class="mobile-meta-val mono">{cred.total_requests ?? 0}</span>
						</div>
						{#if usageSnapshots[cred.id]}
							{@const snap = usageSnapshots[cred.id]}
							<div class="mobile-usage">
								{#if snap.provider === 'claude' && snap.limits}
									<div class="usage-stack">
										<div class="usage-row" title="Unified 5-hour bucket">
											<span class="usage-label mono">5h</span>
											<div class="usage-bar" role="progressbar" aria-label="5 hour usage" aria-valuemin={0} aria-valuemax={100} aria-valuenow={pctNum(snap.limits['unified-5h-utilization'])}>
												<div class="usage-fill fill-5h" style={`width:${pctNum(snap.limits['unified-5h-utilization'])}%`}></div>
											</div>
										</div>
										<div class="usage-meta-row">
											<div class="usage-meta">resets {formatUnixSeconds(snap.limits['unified-5h-reset'])}</div>
											<div class="usage-meta usage-meta-right mono">{pctNum(snap.limits['unified-5h-utilization'])}%</div>
										</div>
										<div class="usage-row" title="Unified weekly bucket">
											<span class="usage-label mono">7d</span>
											<div class="usage-bar" role="progressbar" aria-label="Weekly usage" aria-valuemin={0} aria-valuemax={100} aria-valuenow={pctNum(snap.limits['unified-7d-utilization'])}>
												<div class="usage-fill fill-7d" style={`width:${pctNum(snap.limits['unified-7d-utilization'])}%`}></div>
											</div>
										</div>
										<div class="usage-meta-row">
											<div class="usage-meta">resets {formatUnixSeconds(snap.limits['unified-7d-reset'])}</div>
											<div class="usage-meta usage-meta-right mono">{pctNum(snap.limits['unified-7d-utilization'])}%</div>
										</div>
										<div class="usage-row" title="Weekly bucket (Sonnet only)">
											<span class="usage-label mono">7d s</span>
											<div class="usage-bar" role="progressbar" aria-label="Weekly usage (Sonnet)" aria-valuemin={0} aria-valuemax={100} aria-valuenow={pctNum(snap.limits['unified-7d_sonnet-utilization'])}>
												<div class="usage-fill fill-sonnet" style={`width:${pctNum(snap.limits['unified-7d_sonnet-utilization'])}%`}></div>
											</div>
										</div>
										<div class="usage-meta-row">
											<div class="usage-meta">resets {formatUnixSeconds(snap.limits['unified-7d_sonnet-reset'])}</div>
											<div class="usage-meta usage-meta-right mono">{pctNum(snap.limits['unified-7d_sonnet-utilization'])}%</div>
										</div>
									</div>
								{:else if snap.provider === 'codex' && snap.limits}
									<div class="usage-stack">
										<div class="usage-row" title="Codex 5-hour limit">
											<span class="usage-label mono">5h</span>
											<div class="usage-bar" role="progressbar" aria-label="Codex 5 hour usage" aria-valuemin={0} aria-valuemax={100} aria-valuenow={pctUsedFromLeft(snap.limits['5h_left_pct'])}>
												<div class="usage-fill fill-5h" style={`width:${pctUsedFromLeft(snap.limits['5h_left_pct'])}%`}></div>
											</div>
										</div>
										<div class="usage-meta-row">
											<div class="usage-meta">resets {snap.limits['5h_resets'] || '-'}</div>
											<div class="usage-meta usage-meta-right mono">{pctLeftNum(snap.limits['5h_left_pct'])}% left</div>
										</div>
										<div class="usage-row" title="Codex weekly limit">
											<span class="usage-label mono">7d</span>
											<div class="usage-bar" role="progressbar" aria-label="Codex weekly usage" aria-valuemin={0} aria-valuemax={100} aria-valuenow={pctUsedFromLeft(snap.limits['weekly_left_pct'])}>
												<div class="usage-fill fill-7d" style={`width:${pctUsedFromLeft(snap.limits['weekly_left_pct'])}%`}></div>
											</div>
										</div>
										<div class="usage-meta-row">
											<div class="usage-meta">resets {formatCodexReset(snap.limits['weekly_resets'])}</div>
											<div class="usage-meta usage-meta-right mono">{pctLeftNum(snap.limits['weekly_left_pct'])}% left</div>
										</div>
									</div>
								{:else if snap.provider === 'pi' && snap.limits}
									{#if snap.limits.pi_backend === 'anthropic'}
										<div class="usage-stack">
											<div class="usage-row" title="Unified 5-hour bucket">
												<span class="usage-label mono">5h</span>
												<div class="usage-bar" role="progressbar" aria-label="5 hour usage" aria-valuemin={0} aria-valuemax={100} aria-valuenow={pctNum(snap.limits['unified-5h-utilization'])}>
													<div class="usage-fill fill-5h" style={`width:${pctNum(snap.limits['unified-5h-utilization'])}%`}></div>
												</div>
											</div>
											<div class="usage-meta-row">
												<div class="usage-meta">resets {formatUnixSeconds(snap.limits['unified-5h-reset'])}</div>
												<div class="usage-meta usage-meta-right mono">{pctNum(snap.limits['unified-5h-utilization'])}%</div>
											</div>
											<div class="usage-row" title="Unified weekly bucket">
												<span class="usage-label mono">7d</span>
												<div class="usage-bar" role="progressbar" aria-label="Weekly usage" aria-valuemin={0} aria-valuemax={100} aria-valuenow={pctNum(snap.limits['unified-7d-utilization'])}>
													<div class="usage-fill fill-7d" style={`width:${pctNum(snap.limits['unified-7d-utilization'])}%`}></div>
												</div>
											</div>
											<div class="usage-meta-row">
												<div class="usage-meta">resets {formatUnixSeconds(snap.limits['unified-7d-reset'])}</div>
												<div class="usage-meta usage-meta-right mono">{pctNum(snap.limits['unified-7d-utilization'])}%</div>
											</div>
										</div>
									{:else if snap.limits.pi_backend === 'openai-codex'}
										<div class="usage-stack">
											<div class="usage-row" title="5-hour limit ({snap.limits['primary-window-minutes'] || 300}m)">
												<span class="usage-label mono">5h</span>
												<div class="usage-bar" role="progressbar" aria-label="5 hour usage" aria-valuemin={0} aria-valuemax={100} aria-valuenow={Number(snap.limits['primary-used-percent'] || 0)}>
													<div class="usage-fill fill-5h" style={`width:${snap.limits['primary-used-percent'] || 0}%`}></div>
												</div>
											</div>
											<div class="usage-meta-row">
												<div class="usage-meta">resets {formatUnixSeconds(snap.limits['primary-reset-at'])}</div>
												<div class="usage-meta usage-meta-right mono">{snap.limits['primary-used-percent'] || 0}% used</div>
											</div>
											<div class="usage-row" title="Weekly limit">
												<span class="usage-label mono">7d</span>
												<div class="usage-bar" role="progressbar" aria-label="Weekly usage" aria-valuemin={0} aria-valuemax={100} aria-valuenow={Number(snap.limits['secondary-used-percent'] || 0)}>
													<div class="usage-fill fill-7d" style={`width:${snap.limits['secondary-used-percent'] || 0}%`}></div>
												</div>
											</div>
											<div class="usage-meta-row">
												<div class="usage-meta">resets {formatUnixSeconds(snap.limits['secondary-reset-at'])}</div>
												<div class="usage-meta usage-meta-right mono">{snap.limits['secondary-used-percent'] || 0}% used</div>
											</div>
										</div>
									{:else}
										<span class="dim">No usage data for {snap.limits.pi_backend || 'this backend'}</span>
									{/if}
								{/if}
								<div class="mobile-usage-fetched">fetched {formatCompactDateTime12(new Date(snap.captured_at))}</div>
							</div>
						{/if}
						{#if cred.error_message}
							<div class="mobile-error-msg">{cred.error_message}</div>
						{/if}
						<div class="mobile-cred-actions">
							{#if cred.type === 'oauth'}
								<Button size="sm" variant="ghost" onclick={() => openLogin(cred)} title="Re-authenticate">
									<LogIn size={13} />
									Re-auth
								</Button>
							{/if}
							<Button size="sm" variant="danger" onclick={() => deleteTarget = cred.id}>
								<Trash2 size={13} />
								Delete
							</Button>
						</div>
					</div>
				{/snippet}
				{#snippet header()}
					<th>ID</th>
					<th>Provider</th>
					<th>Type</th>
					<th>Label</th>
					<th>Identity</th>
					<th>Plan</th>
					<th>Status</th>
					<th>Tier</th>
					<th class="col-usage">Usage</th>
					<th class="col-usage-fetched">Usage Fetched At</th>
					<th>Expires</th>
					<th>Last Used</th>
					<th>Requests</th>
					<th>Actions</th>
				{/snippet}
				{#snippet children(cred: Credential)}
					<td class="mono">{cred.id}</td>
					<td>
						<Badge color={cred.provider === 'codex' ? 'accent' : cred.provider === 'pi' ? 'violet' : 'cyan'}>{cred.provider ?? 'claude'}{cred.pi_backend ? ' / ' + cred.pi_backend : ''}</Badge>
					</td>
					<td>
						<Badge color={cred.type === 'oauth' ? 'cyan' : 'accent'}>{cred.type}</Badge>
					</td>
					<td>{cred.label || '-'}</td>
					<td class="mono dim account-id">{cred.account_id || '-'}</td>
					<td>
						{#if cred.plan_type}
							<Badge color="dim">{cred.plan_type}</Badge>
						{:else}
							<span class="dim">-</span>
						{/if}
					</td>
					<td>
						<Badge color={statusColor(cred.status)}>{cred.status}</Badge>
						{#if cred.error_message}
							<div class="error-msg" title={cred.error_message}>{cred.error_message}</div>
						{/if}
					</td>
					<td class="mono dim">{cred.rate_limit_tier || '-'}</td>
					<td class="dim col-usage">
						{#if usageSnapshots[cred.id]}
							{@const snap = usageSnapshots[cred.id]}
							{#if snap.provider === 'claude' && snap.limits}
								<div class="usage-stack">
									<div class="usage-row" title="Unified 5-hour bucket">
										<span class="usage-label mono">5h</span>
										<div class="usage-bar" role="progressbar"
											aria-label="5 hour usage"
											aria-valuemin={0}
											aria-valuemax={100}
											aria-valuenow={pctNum(snap.limits['unified-5h-utilization'])}>
											<div class="usage-fill fill-5h" style={`width:${pctNum(snap.limits['unified-5h-utilization'])}%`}></div>
										</div>
									</div>
									<div class="usage-meta-row">
										<div class="usage-meta">resets {formatUnixSeconds(snap.limits['unified-5h-reset'])}</div>
										<div class="usage-meta usage-meta-right mono">{pctNum(snap.limits['unified-5h-utilization'])}%</div>
									</div>

									<div class="usage-row" title="Unified weekly bucket (all models)">
										<span class="usage-label mono">7d</span>
										<div class="usage-bar" role="progressbar"
											aria-label="Weekly usage (all models)"
											aria-valuemin={0}
											aria-valuemax={100}
											aria-valuenow={pctNum(snap.limits['unified-7d-utilization'])}>
											<div class="usage-fill fill-7d" style={`width:${pctNum(snap.limits['unified-7d-utilization'])}%`}></div>
										</div>
									</div>
									<div class="usage-meta-row">
										<div class="usage-meta">resets {formatUnixSeconds(snap.limits['unified-7d-reset'])}</div>
										<div class="usage-meta usage-meta-right mono">{pctNum(snap.limits['unified-7d-utilization'])}%</div>
									</div>

									<div class="usage-row" title="Weekly bucket (Sonnet only)">
										<span class="usage-label mono">7d s</span>
										<div class="usage-bar" role="progressbar"
											aria-label="Weekly usage (Sonnet only)"
											aria-valuemin={0}
											aria-valuemax={100}
											aria-valuenow={pctNum(snap.limits['unified-7d_sonnet-utilization'])}>
											<div class="usage-fill fill-sonnet" style={`width:${pctNum(snap.limits['unified-7d_sonnet-utilization'])}%`}></div>
										</div>
									</div>
									<div class="usage-meta-row">
										<div class="usage-meta">resets {formatUnixSeconds(snap.limits['unified-7d_sonnet-reset'])}</div>
										<div class="usage-meta usage-meta-right mono">{pctNum(snap.limits['unified-7d_sonnet-utilization'])}%</div>
									</div>
								</div>
							{:else if snap.provider === 'codex' && snap.limits}
								<div class="usage-stack">
									<div class="usage-row" title="Codex 5-hour limit">
										<span class="usage-label mono">5h</span>
										<div class="usage-bar" role="progressbar"
											aria-label="Codex 5 hour usage"
											aria-valuemin={0}
											aria-valuemax={100}
											aria-valuenow={pctUsedFromLeft(snap.limits['5h_left_pct'])}>
											<div class="usage-fill fill-5h" style={`width:${pctUsedFromLeft(snap.limits['5h_left_pct'])}%`}></div>
										</div>
									</div>
									<div class="usage-meta-row">
										<div class="usage-meta">resets {snap.limits['5h_resets'] || '-'}</div>
										<div class="usage-meta usage-meta-right mono">{pctLeftNum(snap.limits['5h_left_pct'])}% left</div>
									</div>

									<div class="usage-row" title="Codex weekly limit">
										<span class="usage-label mono">7d</span>
										<div class="usage-bar" role="progressbar"
											aria-label="Codex weekly usage"
											aria-valuemin={0}
											aria-valuemax={100}
											aria-valuenow={pctUsedFromLeft(snap.limits['weekly_left_pct'])}>
											<div class="usage-fill fill-7d" style={`width:${pctUsedFromLeft(snap.limits['weekly_left_pct'])}%`}></div>
										</div>
									</div>
									<div class="usage-meta-row">
										<div class="usage-meta">resets {formatCodexReset(snap.limits['weekly_resets'])}</div>
										<div class="usage-meta usage-meta-right mono">{pctLeftNum(snap.limits['weekly_left_pct'])}% left</div>
									</div>
								</div>
							{:else if snap.provider === 'pi' && snap.limits}
								{#if snap.limits.pi_backend === 'anthropic'}
									<div class="usage-stack">
										<div class="usage-row" title="Unified 5-hour bucket">
											<span class="usage-label mono">5h</span>
											<div class="usage-bar" role="progressbar" aria-label="5 hour usage" aria-valuemin={0} aria-valuemax={100} aria-valuenow={pctNum(snap.limits['unified-5h-utilization'])}>
												<div class="usage-fill fill-5h" style={`width:${pctNum(snap.limits['unified-5h-utilization'])}%`}></div>
											</div>
										</div>
										<div class="usage-meta-row">
											<div class="usage-meta">resets {formatUnixSeconds(snap.limits['unified-5h-reset'])}</div>
											<div class="usage-meta usage-meta-right mono">{pctNum(snap.limits['unified-5h-utilization'])}%</div>
										</div>
										<div class="usage-row" title="Unified weekly bucket">
											<span class="usage-label mono">7d</span>
											<div class="usage-bar" role="progressbar" aria-label="Weekly usage" aria-valuemin={0} aria-valuemax={100} aria-valuenow={pctNum(snap.limits['unified-7d-utilization'])}>
												<div class="usage-fill fill-7d" style={`width:${pctNum(snap.limits['unified-7d-utilization'])}%`}></div>
											</div>
										</div>
										<div class="usage-meta-row">
											<div class="usage-meta">resets {formatUnixSeconds(snap.limits['unified-7d-reset'])}</div>
											<div class="usage-meta usage-meta-right mono">{pctNum(snap.limits['unified-7d-utilization'])}%</div>
										</div>
									</div>
								{:else if snap.limits.pi_backend === 'openai-codex'}
									<div class="usage-stack">
										<div class="usage-row" title="5-hour limit">
											<span class="usage-label mono">5h</span>
											<div class="usage-bar" role="progressbar"
												aria-label="5 hour usage"
												aria-valuemin={0}
												aria-valuemax={100}
												aria-valuenow={Number(snap.limits['primary-used-percent'] || 0)}>
												<div class="usage-fill fill-5h" style={`width:${snap.limits['primary-used-percent'] || 0}%`}></div>
											</div>
										</div>
										<div class="usage-meta-row">
											<div class="usage-meta">resets {formatUnixSeconds(snap.limits['primary-reset-at'])}</div>
											<div class="usage-meta usage-meta-right mono">{snap.limits['primary-used-percent'] || 0}% used</div>
										</div>
										<div class="usage-row" title="Weekly limit">
											<span class="usage-label mono">7d</span>
											<div class="usage-bar" role="progressbar"
												aria-label="Weekly usage"
												aria-valuemin={0}
												aria-valuemax={100}
												aria-valuenow={Number(snap.limits['secondary-used-percent'] || 0)}>
												<div class="usage-fill fill-7d" style={`width:${snap.limits['secondary-used-percent'] || 0}%`}></div>
											</div>
										</div>
										<div class="usage-meta-row">
											<div class="usage-meta">resets {formatUnixSeconds(snap.limits['secondary-reset-at'])}</div>
											<div class="usage-meta usage-meta-right mono">{snap.limits['secondary-used-percent'] || 0}% used</div>
										</div>
									</div>
								{:else}
									<span class="dim">No usage data for {snap.limits.pi_backend || 'this backend'}</span>
								{/if}
							{:else}
								<span class="dim">-</span>
							{/if}
						{:else}
							<span class="dim">-</span>
						{/if}
					</td>
					<td class="dim col-usage-fetched">
						{#if usageSnapshots[cred.id]}
							{@const snap = usageSnapshots[cred.id]}
							<span title={snap.captured_at}>{formatCompactDateTime24(new Date(snap.captured_at))}</span>
						{:else}
							-
						{/if}
					</td>
					<td class="dim">{formatDate(cred.expires_at)}</td>
					<td class="dim">{formatDate(cred.last_used_at)}</td>
					<td>{cred.total_requests ?? 0}</td>
					<td>
						<div class="actions">
							{#if cred.type === 'oauth'}
								<Button size="sm" variant="ghost" onclick={() => openLogin(cred)} title="Re-authenticate">
									<LogIn size={13} />
								</Button>
							{/if}
							<Button size="sm" variant="danger" onclick={() => deleteTarget = cred.id}>
								<Trash2 size={13} />
							</Button>
						</div>
					</td>
				{/snippet}
			</DataTable>
		</Card>
	{/if}
</div>

<!-- Add Credential Modal -->
<Modal open={showAdd} title="Add Credential" onclose={() => { showAdd = false; resetAddForm(); }}>
	<div class="form-stack">
		<FormField label="ID">
			<input type="text" bind:value={addForm.id} placeholder="cred-1" />
		</FormField>
		<FormField label="Type">
			<select bind:value={addForm.type}>
				<option value="oauth">OAuth</option>
				<option value="apikey">API Key</option>
			</select>
		</FormField>
		<FormField label="Provider">
			<select bind:value={addForm.provider} onchange={() => {
				if (addForm.provider === 'codex' && addForm.type === 'oauth') authorizeUrl = '';
			}}>
				<option value="claude">Claude</option>
				<option value="codex">Codex</option>
				<option value="pi">Pi (OAuth auth.json)</option>
			</select>
		</FormField>
		{#if addForm.provider === 'pi'}
		<FormField label="Pi Backend">
			{#if loadingPiProviders}
				<span class="hint">Loading providers...</span>
			{:else}
			<select bind:value={addForm.pi_backend}>
				<option value="">All providers (whole auth.json)</option>
				{#each piProviders as p}
					<option value={p.id}>{p.label}</option>
				{/each}
			</select>
			{/if}
		</FormField>
		{/if}
		<FormField label="Label">
			<input type="text" bind:value={addForm.label} placeholder="Optional label" />
		</FormField>
		<FormField label="Plan Type">
			<select bind:value={addForm.plan_type}>
				<option value="">Default</option>
				<option value="free">Free</option>
				<option value="plus">Plus</option>
				<option value="team">Team</option>
				<option value="pro">Pro</option>
				<option value="enterprise">Enterprise</option>
			</select>
		</FormField>
		{#if addForm.type === 'apikey'}
			<FormField label="API Key">
				<input type="password" bind:value={addForm.api_key} placeholder={addForm.provider === 'codex' ? 'sk-...' : 'sk-ant-...'} />
			</FormField>
		{:else if addForm.provider === 'codex'}
			<div class="auth-url-box">
				<p class="auth-label">Codex OAuth import</p>
				<p class="confirm-text">This imports the current host login from <strong class="mono">~/.codex/auth.json</strong>. Run <strong class="mono">codex login</strong> on the host first if needed.</p>
			</div>
		{:else if addForm.provider === 'pi'}
			<div class="auth-url-box">
				<p class="auth-label">Pi OAuth</p>
				{#if addForm.pi_backend}
					<p class="confirm-text">After creating, the OAuth login flow will open automatically for <strong>{addForm.pi_backend}</strong>. A browser tab will open for authentication.</p>
				{:else}
					<p class="confirm-text">Select a Pi backend above, then click Create to start the OAuth flow. Or leave blank to import the full <strong class="mono">~/.pi/agent/auth.json</strong> from the host.</p>
				{/if}
			</div>
		{/if}
		{#if authorizeUrl}
			<div class="auth-url-box">
				<p class="auth-label">Authorize this credential:</p>
				<a href={authorizeUrl} target="_blank" rel="noopener" class="auth-url">{authorizeUrl}</a>
			</div>
		{/if}
	</div>
	{#snippet footer()}
		<Button variant="secondary" onclick={() => { showAdd = false; resetAddForm(); }}>Cancel</Button>
		<Button variant="primary" loading={adding} onclick={handleAdd}>Create</Button>
	{/snippet}
</Modal>

<!-- Login / Re-auth Modal -->
<Modal open={loginTarget !== null} title="Re-authenticate Credential" onclose={() => { loginTarget = null; stopCodexPoll(); stopPiPoll(); }}>
	{#if loginTarget}
		<div class="form-stack">
			<div class="dim">Credential: <strong class="mono">{loginTarget.id}</strong> · {loginTarget.provider ?? 'claude'}</div>
			{#if (loginTarget.provider ?? 'claude') === 'claude'}
				{#if loginAuthorizeUrl}
					<div class="auth-url-box">
						<p class="auth-label">Authorize URL</p>
						<a href={loginAuthorizeUrl} target="_blank" rel="noopener" class="auth-url">{loginAuthorizeUrl}</a>
						<p class="dim" style="margin-top:6px;">After authorizing, paste the callback code here.</p>
					</div>
				{/if}
				<FormField label="Callback code">
					<input type="text" bind:value={callbackCode} placeholder="Paste code from callback page" />
				</FormField>
			{:else if (loginTarget.provider ?? 'claude') === 'codex'}
				<div class="auth-url-box">
					<p class="auth-label">Codex device auth</p>
					{#if codexDeviceUrl && codexUserCode}
						<p class="confirm-text">A browser tab has been opened. Sign in and enter the one-time code below when prompted. The credential will update automatically once complete.</p>
						<p class="dim" style="margin-top:8px;">Authorize URL</p>
						<a href={codexDeviceUrl} target="_blank" rel="noopener" class="auth-url">{codexDeviceUrl}</a>
						<p class="dim" style="margin-top:10px;">One-time code</p>
						<div class="mono" style="font-size: var(--font-size-lg); letter-spacing: 0.08em;">{codexUserCode}</div>
					{:else if loggingIn}
						<p class="confirm-text">Starting device auth…</p>
					{:else}
						<p class="confirm-text">Click <strong>Start</strong> to generate an authorization URL and one-time code.</p>
					{/if}
				</div>
			{:else if (loginTarget.provider ?? 'claude') === 'pi'}
				<div class="auth-url-box">
					<p class="auth-label">Pi OAuth login — {loginTarget.pi_backend || 'unknown backend'}</p>
					{#if piLoginUrl}
						<p class="dim" style="margin-top:4px;">A browser tab has been opened. Complete sign-in there.</p>
						<p class="dim" style="margin-top:8px;">Authorize URL</p>
						<a href={piLoginUrl} target="_blank" rel="noopener" class="auth-url">{piLoginUrl}</a>
						{#if piNeedsManualCode}
							<p class="dim" style="margin-top:12px;">{piLoginInstructions || 'After authenticating, copy the code or callback URL and paste it below.'}</p>
							<FormField label="Authentication code">
								<input type="text" bind:value={piManualCode} placeholder="code#state or http://localhost:…" />
							</FormField>
						{/if}
						{#if piLoginProgress.length > 0}
							<div class="progress-log" style="margin-top:12px;">
								{#each piLoginProgress.filter(p => !p.startsWith('http')) as line}
									<p class="dim" style="margin:2px 0;">{line}</p>
								{/each}
							</div>
						{/if}
					{:else if loggingIn}
						<p class="confirm-text">Starting OAuth flow for <strong>{loginTarget.pi_backend}</strong>…</p>
					{:else}
						<p class="confirm-text">Click <strong>Start</strong> to launch the OAuth flow for <strong>{loginTarget.pi_backend || 'this backend'}</strong>. A browser tab will open for authentication.</p>
					{/if}
				</div>
			{/if}
		</div>
	{/if}
	{#snippet footer()}
		<Button variant="secondary" onclick={() => { loginTarget = null; stopCodexPoll(); }}>Cancel</Button>
		{#if loginTarget && (loginTarget.provider ?? 'claude') === 'claude'}
			<Button variant="primary" loading={loggingIn} onclick={completeClaudeLogin}>Finish Login</Button>
		{:else if loginTarget && (loginTarget.provider ?? 'claude') === 'codex'}
			{#if !codexDeviceUrl}
				<Button variant="primary" loading={loggingIn} onclick={() => loginTarget && openLogin(loginTarget)}>Start</Button>
			{:else}
				<Button variant="secondary" loading={loggingIn} onclick={completeCodexImport}>Check Now</Button>
			{/if}
		{:else if loginTarget && (loginTarget.provider ?? 'claude') === 'pi'}
			{#if !piLoginUrl}
				<Button variant="primary" loading={loggingIn} onclick={() => loginTarget && openLogin(loginTarget)}>Start</Button>
			{:else if piNeedsManualCode}
				<Button variant="primary" loading={piSubmittingCode} disabled={!piManualCode.trim()} onclick={submitPiCode}>Submit Code</Button>
			{:else}
				<Button variant="secondary" disabled>Waiting for OAuth callback…</Button>
			{/if}
		{/if}
	{/snippet}
</Modal>

<!-- Delete Confirmation Modal -->
<Modal open={deleteTarget !== null} title="Delete Credential" onclose={() => deleteTarget = null}>
	<p class="confirm-text">Are you sure you want to delete credential <strong class="mono">{deleteTarget}</strong>? This cannot be undone.</p>
	{#snippet footer()}
		<Button variant="secondary" onclick={() => deleteTarget = null}>Cancel</Button>
		<Button variant="danger" loading={deleting} onclick={handleDelete}>Delete</Button>
	{/snippet}
</Modal>

<style>
	.page {
		display: flex;
		flex-direction: column;
		gap: 20px;
	}
	.page-header {
		display: flex;
		align-items: center;
		justify-content: space-between;
	}
	.page-title {
		display: flex;
		align-items: center;
		gap: 10px;
		color: var(--text-bright);
	}
	.page-title h1 {
		font-size: var(--font-size-xl);
		font-weight: 700;
	}
	.center {
		display: flex;
		justify-content: center;
		padding: 60px 0;
	}
	.mono {
		font-family: var(--font-mono);
	}
	.dim {
		color: var(--text-dim);
		font-size: var(--font-size-xs);
	}
	.actions {
		display: flex;
		gap: 4px;
	}
	.toolbar {
		display: flex;
		gap: 8px;
		justify-content: flex-end;
		margin-bottom: 10px;
	}
	.usage-stack {
		display: flex;
		flex-direction: column;
		gap: 6px;
		width: 100%;
	}
	.usage-row {
		display: grid;
		grid-template-columns: 38px 1fr;
		align-items: center;
		gap: 8px;
		width: 100%;
	}
	.usage-label {
		color: var(--text-muted);
		font-size: var(--font-size-xs);
		letter-spacing: 0.06em;
		text-transform: uppercase;
	}
	.usage-bar {
		height: 10px;
		width: 100%;
		min-width: 0;
		border-radius: 999px;
		background: linear-gradient(180deg, var(--surface-3), var(--surface-2));
		border: 1px solid var(--border-subtle);
		box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
		overflow: hidden;
		position: relative;
	}
	.usage-fill {
		height: 100%;
		border-radius: 999px;
		box-shadow: 0 0 0 1px rgba(0,0,0,0.12), 0 8px 22px rgba(99, 102, 241, 0.18);
	}
	.usage-bar-text {
		position: absolute;
		right: 8px;
		top: 50%;
		transform: translateY(-50%);
		font-size: var(--font-size-xxs, 11px);
		color: rgba(255, 255, 255, 0.72);
		text-shadow: 0 1px 0 rgba(0, 0, 0, 0.5);
		pointer-events: none;
		white-space: nowrap;
	}
	.fill-5h {
		background: linear-gradient(90deg, rgba(99,102,241,0.35), rgba(99,102,241,0.95));
	}
	.fill-7d {
		background: linear-gradient(90deg, rgba(34,211,238,0.25), rgba(34,211,238,0.9));
		box-shadow: 0 0 0 1px rgba(0,0,0,0.12), 0 8px 22px rgba(34, 211, 238, 0.16);
	}
	.fill-sonnet {
		background: linear-gradient(90deg, rgba(52,211,153,0.25), rgba(52,211,153,0.9));
		box-shadow: 0 0 0 1px rgba(0,0,0,0.12), 0 8px 22px rgba(52, 211, 153, 0.14);
	}
	.usage-meta {
		margin-left: 46px;
		margin-top: -4px;
		font-size: var(--font-size-xxs, 11px);
		line-height: 1.15;
		color: var(--text-muted);
	}
	.usage-meta-row {
		display: flex;
		justify-content: space-between;
		align-items: baseline;
		gap: 10px;
	}
	.usage-meta-right {
		margin-left: 0;
		text-align: right;
		white-space: nowrap;
		color: var(--text-muted);
	}
	.form-stack {
		display: flex;
		flex-direction: column;
		gap: 14px;
	}
	.confirm-text {
		color: var(--text);
		line-height: 1.5;
	}
	.auth-url-box {
		background: var(--surface-0);
		border: 1px solid var(--border);
		border-radius: var(--radius);
		padding: 12px;
	}
	.auth-label {
		font-size: var(--font-size-sm);
		color: var(--text-dim);
		margin-bottom: 6px;
	}
	.auth-url {
		font-family: var(--font-mono);
		font-size: var(--font-size-xs);
		color: var(--accent);
		word-break: break-all;
	}
	.error-msg {
		font-size: var(--font-size-xs);
		color: var(--rose, #f87171);
		margin-top: 3px;
		max-width: 200px;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}

	:global(td.account-id) {
		max-width: 160px;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
		font-size: var(--font-size-xs);
	}

	:global(th.col-usage),
	:global(td.col-usage) {
		min-width: 260px;
	}
	:global(th.col-usage-fetched),
	:global(td.col-usage-fetched) {
		min-width: 110px;
	}

	.mobile-cred-card {
		padding: 14px 16px;
		border-bottom: 1px solid var(--border-subtle);
		display: flex;
		flex-direction: column;
		gap: 8px;
	}
	.mobile-cred-top {
		display: flex;
		align-items: flex-start;
		justify-content: space-between;
		gap: 8px;
		flex-wrap: wrap;
	}
	.mobile-cred-id {
		font-family: var(--font-mono);
		font-size: var(--font-size-sm);
		color: var(--accent);
		font-weight: 600;
		word-break: break-all;
	}
	.mobile-cred-badges {
		display: flex;
		gap: 4px;
		flex-wrap: wrap;
		flex-shrink: 0;
	}
	.mobile-cred-label {
		font-size: var(--font-size-sm);
		color: var(--text);
	}
	.mobile-cred-meta {
		display: flex;
		align-items: center;
		gap: 8px;
		font-size: var(--font-size-xs);
	}
	.mobile-meta-key {
		color: var(--text-muted);
		min-width: 56px;
	}
	.mobile-meta-val {
		color: var(--text-dim);
	}
	.mobile-error-msg {
		font-size: var(--font-size-xs);
		color: var(--rose, #f87171);
		word-break: break-word;
	}
	.mobile-usage {
		display: flex;
		flex-direction: column;
		gap: 2px;
		padding-top: 2px;
	}
	.mobile-usage-fetched {
		font-size: var(--font-size-xxs, 11px);
		color: var(--text-muted);
		margin-top: 2px;
	}
	.mobile-cred-actions {
		display: flex;
		gap: 6px;
		margin-top: 2px;
	}
</style>
