<script lang="ts">
	export interface SessionManagerTranslations {
		title?: string;
		subtitle?: string;
		current?: string;
		revoke?: string;
		revokeAll?: string;
		lastActivity?: string;
		confirmRevoke?: string;
		confirmRevokeAll?: string;
		noSessions?: string;
		unknown?: string;
	}

	interface Session {
		id: string;
		ipAddress: string | null;
		userAgent: string | null;
		deviceId: string | null;
		deviceName: string | null;
		lastActivityAt: string | null;
		createdAt: string;
		expiresAt: string;
	}

	interface Props {
		sessions: Session[];
		currentSessionId?: string;
		onRevoke: (sessionId: string) => Promise<{ success: boolean; error?: string }>;
		onRefresh: () => Promise<void>;
		loading?: boolean;
		primaryColor?: string;
		translations?: SessionManagerTranslations;
	}

	const defaultTranslations: Required<SessionManagerTranslations> = {
		title: 'Aktive Sitzungen',
		subtitle: 'Geräte, die aktuell angemeldet sind',
		current: 'Aktuell',
		revoke: 'Abmelden',
		revokeAll: 'Alle anderen Sitzungen beenden',
		lastActivity: 'Letzte Aktivität',
		confirmRevoke: 'Sitzung wirklich beenden?',
		confirmRevokeAll: 'Alle anderen Sitzungen wirklich beenden?',
		noSessions: 'Keine aktiven Sitzungen gefunden.',
		unknown: 'Unbekanntes Gerät',
	};

	let {
		sessions,
		currentSessionId,
		onRevoke,
		onRefresh,
		loading = false,
		primaryColor = '#6366f1',
		translations,
	}: Props = $props();

	let t = $derived({ ...defaultTranslations, ...translations });
	let revoking = $state<string | null>(null);
	let revokingAll = $state(false);
	let error = $state<string | null>(null);

	function parseUserAgent(ua: string | null): { browser: string; os: string } {
		if (!ua) return { browser: '', os: '' };

		let browser = '';
		let os = '';

		if (ua.includes('Firefox/')) browser = 'Firefox';
		else if (ua.includes('Edg/')) browser = 'Edge';
		else if (ua.includes('Chrome/') && !ua.includes('Edg/')) browser = 'Chrome';
		else if (ua.includes('Safari/') && !ua.includes('Chrome/')) browser = 'Safari';
		else if (ua.includes('Opera/') || ua.includes('OPR/')) browser = 'Opera';

		if (ua.includes('Windows')) os = 'Windows';
		else if (ua.includes('Mac OS X') || ua.includes('Macintosh')) os = 'macOS';
		else if (ua.includes('Linux') && !ua.includes('Android')) os = 'Linux';
		else if (ua.includes('Android')) os = 'Android';
		else if (ua.includes('iPhone') || ua.includes('iPad')) os = 'iOS';

		return { browser, os };
	}

	function getDeviceType(ua: string | null): 'mobile' | 'desktop' | 'tablet' {
		if (!ua) return 'desktop';
		if (ua.includes('iPhone') || (ua.includes('Android') && !ua.includes('Tablet')))
			return 'mobile';
		if (ua.includes('iPad') || ua.includes('Tablet')) return 'tablet';
		return 'desktop';
	}

	function formatRelativeTime(dateStr: string | null): string {
		if (!dateStr) return '';
		const date = new Date(dateStr);
		const now = new Date();
		const diffMs = now.getTime() - date.getTime();
		const diffSec = Math.floor(diffMs / 1000);
		const diffMin = Math.floor(diffSec / 60);
		const diffHours = Math.floor(diffMin / 60);
		const diffDays = Math.floor(diffHours / 24);

		if (diffSec < 60) return 'gerade eben';
		if (diffMin < 60) return `vor ${diffMin} Min`;
		if (diffHours < 24) return `vor ${diffHours} Std`;
		if (diffDays === 1) return 'Gestern';
		if (diffDays < 7) return `vor ${diffDays} Tagen`;

		return date.toLocaleDateString('de-DE', {
			day: '2-digit',
			month: '2-digit',
			year: 'numeric',
		});
	}

	function getSessionLabel(session: Session): string {
		if (session.deviceName) return session.deviceName;
		const { browser, os } = parseUserAgent(session.userAgent);
		const parts = [browser, os].filter(Boolean);
		return parts.length > 0 ? parts.join(' \u00b7 ') : t.unknown;
	}

	function isCurrent(session: Session): boolean {
		if (!currentSessionId) return false;
		return session.id === currentSessionId;
	}

	async function handleRevoke(sessionId: string) {
		if (!confirm(t.confirmRevoke)) return;
		error = null;
		revoking = sessionId;
		try {
			const result = await onRevoke(sessionId);
			if (!result.success) {
				error = result.error || 'Fehler beim Beenden der Sitzung';
			} else {
				await onRefresh();
			}
		} catch (e) {
			error = e instanceof Error ? e.message : 'Fehler beim Beenden der Sitzung';
		} finally {
			revoking = null;
		}
	}

	async function handleRevokeAll() {
		if (!confirm(t.confirmRevokeAll)) return;
		error = null;
		revokingAll = true;
		try {
			const otherSessions = sessions.filter((s) => !isCurrent(s));
			for (const session of otherSessions) {
				await onRevoke(session.id);
			}
			await onRefresh();
		} catch (e) {
			error = e instanceof Error ? e.message : 'Fehler beim Beenden der Sitzungen';
		} finally {
			revokingAll = false;
		}
	}

	let otherSessionCount = $derived(
		currentSessionId ? sessions.filter((s) => !isCurrent(s)).length : sessions.length - 1
	);
</script>

<div class="session-manager" style:--primary-color={primaryColor}>
	<div class="session-header">
		<div class="session-header-left">
			<div class="session-icon">
				<svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
					<path
						stroke-linecap="round"
						stroke-linejoin="round"
						stroke-width="2"
						d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
					/>
				</svg>
			</div>
			<div>
				<h3 class="session-title">{t.title} ({sessions.length})</h3>
				<p class="session-subtitle">{t.subtitle}</p>
			</div>
		</div>
		<button
			type="button"
			class="refresh-button"
			onclick={onRefresh}
			disabled={loading}
			aria-label="Aktualisieren"
		>
			<svg
				class="refresh-icon"
				class:spinning={loading}
				fill="none"
				stroke="currentColor"
				viewBox="0 0 24 24"
			>
				<path
					stroke-linecap="round"
					stroke-linejoin="round"
					stroke-width="2"
					d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
				/>
			</svg>
		</button>
	</div>

	{#if error}
		<div class="error-message">{error}</div>
	{/if}

	{#if loading && sessions.length === 0}
		<div class="loading-state">
			<div class="loading-spinner"></div>
		</div>
	{:else if sessions.length === 0}
		<p class="empty-state">{t.noSessions}</p>
	{:else}
		<div class="session-list">
			{#each sessions as session (session.id)}
				{@const current = isCurrent(session)}
				{@const deviceType = getDeviceType(session.userAgent)}
				<div class="session-item" class:session-current={current}>
					<div class="session-device-icon">
						{#if deviceType === 'mobile'}
							<svg class="device-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
								<path
									stroke-linecap="round"
									stroke-linejoin="round"
									stroke-width="1.5"
									d="M12 18h.01M8 21h8a2 2 0 002-2V5a2 2 0 00-2-2H8a2 2 0 00-2 2v14a2 2 0 002 2z"
								/>
							</svg>
						{:else if deviceType === 'tablet'}
							<svg class="device-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
								<path
									stroke-linecap="round"
									stroke-linejoin="round"
									stroke-width="1.5"
									d="M12 18h.01M7 21h10a2 2 0 002-2V5a2 2 0 00-2-2H7a2 2 0 00-2 2v14a2 2 0 002 2z"
								/>
							</svg>
						{:else}
							<svg class="device-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
								<path
									stroke-linecap="round"
									stroke-linejoin="round"
									stroke-width="1.5"
									d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
								/>
							</svg>
						{/if}
					</div>
					<div class="session-content">
						<div class="session-label">
							<span class="session-name">{getSessionLabel(session)}</span>
							{#if current}
								<span class="current-badge">{t.current}</span>
							{/if}
						</div>
						{#if session.ipAddress}
							<div class="session-ip">{session.ipAddress}</div>
						{/if}
						{#if session.lastActivityAt}
							<div class="session-activity">
								{t.lastActivity}: {formatRelativeTime(session.lastActivityAt)}
							</div>
						{/if}
					</div>
					{#if !current}
						<button
							type="button"
							class="revoke-button"
							onclick={() => handleRevoke(session.id)}
							disabled={revoking === session.id || revokingAll}
						>
							{#if revoking === session.id}
								<span class="revoke-spinner"></span>
							{:else}
								{t.revoke}
							{/if}
						</button>
					{/if}
				</div>
			{/each}
		</div>

		{#if otherSessionCount > 0}
			<button
				type="button"
				class="revoke-all-button"
				onclick={handleRevokeAll}
				disabled={revokingAll}
			>
				{#if revokingAll}
					<span class="revoke-spinner"></span>
				{:else}
					{t.revokeAll}
				{/if}
			</button>
		{/if}
	{/if}
</div>

<style>
	.session-manager {
		width: 100%;
	}

	.session-header {
		display: flex;
		align-items: center;
		justify-content: space-between;
		margin-bottom: 1.5rem;
	}

	.session-header-left {
		display: flex;
		align-items: center;
		gap: 0.75rem;
	}

	.session-icon {
		display: flex;
		height: 2.5rem;
		width: 2.5rem;
		align-items: center;
		justify-content: center;
		border-radius: 9999px;
		background-color: color-mix(in srgb, var(--primary-color) 15%, transparent);
		color: var(--primary-color);
	}

	.session-icon .icon {
		height: 1.25rem;
		width: 1.25rem;
	}

	.session-title {
		font-size: 1.125rem;
		font-weight: 600;
		margin: 0;
	}

	.session-subtitle {
		font-size: 0.875rem;
		color: var(--text-muted, #9ca3af);
		margin: 0;
	}

	.refresh-button {
		display: flex;
		align-items: center;
		justify-content: center;
		width: 2.25rem;
		height: 2.25rem;
		border-radius: 0.5rem;
		border: 1px solid var(--border-color, #e5e7eb);
		background: transparent;
		color: var(--text-muted, #9ca3af);
		cursor: pointer;
		transition: all 0.2s;
	}

	.refresh-button:hover:not(:disabled) {
		background: var(--hover-bg, #f3f4f6);
		color: var(--text-primary, #111827);
	}

	.refresh-button:disabled {
		opacity: 0.5;
		cursor: not-allowed;
	}

	:global(.dark) .refresh-button {
		border-color: rgba(255, 255, 255, 0.1);
	}

	:global(.dark) .refresh-button:hover:not(:disabled) {
		background: rgba(255, 255, 255, 0.05);
		color: #fff;
	}

	.refresh-icon {
		width: 1.125rem;
		height: 1.125rem;
	}

	.spinning {
		animation: spin 1s linear infinite;
	}

	@keyframes spin {
		to {
			transform: rotate(360deg);
		}
	}

	.error-message {
		padding: 0.75rem 1rem;
		margin-bottom: 1rem;
		border-radius: 0.5rem;
		font-size: 0.875rem;
		background-color: #fef2f2;
		color: #dc2626;
		border: 1px solid #fecaca;
	}

	:global(.dark) .error-message {
		background-color: rgba(220, 38, 38, 0.1);
		border-color: rgba(220, 38, 38, 0.2);
		color: #fca5a5;
	}

	.loading-state {
		display: flex;
		justify-content: center;
		padding: 2rem;
	}

	.loading-spinner {
		width: 2rem;
		height: 2rem;
		border: 3px solid var(--border-color, #e5e7eb);
		border-top-color: var(--primary-color);
		border-radius: 50%;
		animation: spin 0.8s linear infinite;
	}

	.empty-state {
		text-align: center;
		padding: 2rem;
		color: var(--text-muted, #9ca3af);
		font-size: 0.875rem;
	}

	.session-list {
		display: flex;
		flex-direction: column;
		border: 1px solid var(--border-color, #e5e7eb);
		border-radius: 0.75rem;
		overflow: hidden;
	}

	:global(.dark) .session-list {
		border-color: rgba(255, 255, 255, 0.1);
	}

	.session-item {
		display: flex;
		align-items: flex-start;
		gap: 0.75rem;
		padding: 1rem;
		border-bottom: 1px solid var(--border-color, #e5e7eb);
	}

	:global(.dark) .session-item {
		border-bottom-color: rgba(255, 255, 255, 0.06);
	}

	.session-item:last-child {
		border-bottom: none;
	}

	.session-current {
		background-color: color-mix(in srgb, var(--primary-color) 5%, transparent);
	}

	.session-device-icon {
		display: flex;
		flex-shrink: 0;
		align-items: center;
		justify-content: center;
		width: 2.25rem;
		height: 2.25rem;
		border-radius: 0.5rem;
		background-color: var(--hover-bg, #f3f4f6);
		color: var(--text-muted, #6b7280);
	}

	:global(.dark) .session-device-icon {
		background-color: rgba(255, 255, 255, 0.06);
		color: #9ca3af;
	}

	.device-icon {
		width: 1.25rem;
		height: 1.25rem;
	}

	.session-content {
		flex: 1;
		min-width: 0;
	}

	.session-label {
		display: flex;
		align-items: center;
		gap: 0.5rem;
	}

	.session-name {
		font-size: 0.875rem;
		font-weight: 500;
	}

	.current-badge {
		display: inline-flex;
		align-items: center;
		gap: 0.25rem;
		font-size: 0.6875rem;
		font-weight: 600;
		text-transform: uppercase;
		letter-spacing: 0.03em;
		padding: 0.125rem 0.5rem;
		border-radius: 9999px;
		background-color: color-mix(in srgb, var(--primary-color) 15%, transparent);
		color: var(--primary-color);
	}

	.session-ip {
		font-size: 0.75rem;
		color: var(--text-muted, #9ca3af);
		margin-top: 0.125rem;
	}

	.session-activity {
		font-size: 0.75rem;
		color: var(--text-muted, #9ca3af);
		opacity: 0.8;
	}

	.revoke-button {
		flex-shrink: 0;
		align-self: center;
		padding: 0.375rem 0.75rem;
		font-size: 0.8125rem;
		font-weight: 500;
		border-radius: 0.375rem;
		border: 1px solid var(--border-color, #e5e7eb);
		background: transparent;
		color: #dc2626;
		cursor: pointer;
		transition: all 0.15s;
		min-width: 5rem;
		display: flex;
		align-items: center;
		justify-content: center;
	}

	.revoke-button:hover:not(:disabled) {
		background-color: #fef2f2;
		border-color: #fecaca;
	}

	:global(.dark) .revoke-button {
		border-color: rgba(255, 255, 255, 0.1);
		color: #fca5a5;
	}

	:global(.dark) .revoke-button:hover:not(:disabled) {
		background-color: rgba(220, 38, 38, 0.1);
		border-color: rgba(220, 38, 38, 0.2);
	}

	.revoke-button:disabled {
		opacity: 0.5;
		cursor: not-allowed;
	}

	.revoke-all-button {
		display: flex;
		align-items: center;
		justify-content: center;
		width: 100%;
		margin-top: 0.75rem;
		padding: 0.625rem 1rem;
		font-size: 0.875rem;
		font-weight: 500;
		border-radius: 0.5rem;
		border: 1px solid #fecaca;
		background: #fef2f2;
		color: #dc2626;
		cursor: pointer;
		transition: all 0.15s;
	}

	.revoke-all-button:hover:not(:disabled) {
		background-color: #fee2e2;
		border-color: #fca5a5;
	}

	:global(.dark) .revoke-all-button {
		background-color: rgba(220, 38, 38, 0.1);
		border-color: rgba(220, 38, 38, 0.2);
		color: #fca5a5;
	}

	:global(.dark) .revoke-all-button:hover:not(:disabled) {
		background-color: rgba(220, 38, 38, 0.15);
	}

	.revoke-all-button:disabled {
		opacity: 0.5;
		cursor: not-allowed;
	}

	.revoke-spinner {
		display: inline-block;
		width: 1rem;
		height: 1rem;
		border: 2px solid currentColor;
		border-top-color: transparent;
		border-radius: 50%;
		animation: spin 0.6s linear infinite;
	}

	@media (prefers-reduced-motion: reduce) {
		.spinning {
			animation: none;
		}
		.loading-spinner {
			animation: none;
		}
		.revoke-spinner {
			animation: none;
		}
	}
</style>
