<script lang="ts">
	import { onDestroy, onMount } from 'svelte';
	import { browser } from '$app/environment';
	import { m } from '$lib/paraglide/messages';

	// Geolocation
	let geoWatchId: number | null = $state(null);
	let geolocation: GeolocationPosition | null = $state(null);
	let geoError: string | null = $state(null);
	let geoSupported = $state<boolean>(false);

	// DeviceMotion / DeviceOrientation (useful fallbacks on iOS/Safari)
	type Motion = { ax?: number; ay?: number; az?: number; gx?: number; gy?: number; gz?: number };
	let deviceMotion: Motion | null = $state(null);
	let deviceOrientation: { alpha?: number; beta?: number; gamma?: number } | null = $state(null);
	let motionSupported = $state(false);
	let orientationSupported = $state(false);

	// iOS Safari permission flow for motion/orientation
	let motionPermissionAvailable = $state(false);
	let orientationPermissionAvailable = $state(false);
	let motionPermission: 'unknown' | 'granted' | 'denied' = $state('unknown');
	let orientationPermission: 'unknown' | 'granted' | 'denied' = $state('unknown');

	// Generic Sensor API (subject to browser/flag support)
	type SensorHandle = {
		instance?: any;
		supported: boolean;
		active: boolean;
		error?: string | null;
		data: Record<string, number | string | undefined>;
	};

	let accelerometer: SensorHandle = $state({ supported: false, active: false, data: {} });
	let gyroscope: SensorHandle = $state({ supported: false, active: false, data: {} });
	let magnetometer: SensorHandle = $state({ supported: false, active: false, data: {} });
	let ambientLight: SensorHandle = $state({ supported: false, active: false, data: {} });
	let barometer: SensorHandle = $state({ supported: false, active: false, data: {} });

	const w = browser ? (window as any) : undefined;

	function detectSupport() {
		geoSupported = browser && 'geolocation' in navigator;
		motionSupported = browser && 'DeviceMotionEvent' in (window as any);
		orientationSupported = browser && 'DeviceOrientationEvent' in (window as any);

		accelerometer.supported = Boolean(w?.Accelerometer);
		gyroscope.supported = Boolean(w?.Gyroscope);
		magnetometer.supported = Boolean(w?.Magnetometer);
		ambientLight.supported = Boolean(w?.AmbientLightSensor);
		barometer.supported = Boolean(w?.Barometer);
	}

	onMount(() => {
		detectSupport();

		// Check for iOS-style permission request APIs
		motionPermissionAvailable =
			browser &&
			typeof (window as any).DeviceMotionEvent !== 'undefined' &&
			typeof (DeviceMotionEvent as any).requestPermission === 'function';
		orientationPermissionAvailable =
			browser &&
			typeof (window as any).DeviceOrientationEvent !== 'undefined' &&
			typeof (DeviceOrientationEvent as any).requestPermission === 'function';

		if (orientationSupported) {
			const handler = (e: DeviceOrientationEvent) => {
				deviceOrientation = {
					alpha: e.alpha ?? undefined,
					beta: e.beta ?? undefined,
					gamma: e.gamma ?? undefined
				};
			};
			window.addEventListener('deviceorientation', handler);
			onDestroy(() => window.removeEventListener('deviceorientation', handler));
		}

		if (motionSupported) {
			const handler = (e: DeviceMotionEvent) => {
				deviceMotion = {
					ax: e.acceleration?.x ?? undefined,
					ay: e.acceleration?.y ?? undefined,
					az: e.acceleration?.z ?? undefined,
					gx: e.rotationRate?.alpha ?? undefined,
					gy: e.rotationRate?.beta ?? undefined,
					gz: e.rotationRate?.gamma ?? undefined
				};
			};
			window.addEventListener('devicemotion', handler);
			onDestroy(() => window.removeEventListener('devicemotion', handler));
		}
	});

	onDestroy(() => {
		stopGeolocation();
		stopSensor(accelerometer);
		stopSensor(gyroscope);
		stopSensor(magnetometer);
		stopSensor(ambientLight);
		stopSensor(barometer);
	});

	// (Permissions are requested implicitly when starting sensors where applicable)

	// Geolocation controls
	async function startGeolocation() {
		if (!geoSupported) return;
		try {
			geoError = null;
			geolocation = await new Promise<GeolocationPosition>((resolve, reject) => {
				navigator.geolocation.getCurrentPosition(resolve, reject, {
					enableHighAccuracy: true,
					timeout: 10000
				});
			});
			geoWatchId = navigator.geolocation.watchPosition(
				(pos) => (geolocation = pos),
				(err) => (geoError = err?.message || String(err)),
				{ enableHighAccuracy: true }
			);
		} catch (e: any) {
			geoError = e?.message ?? String(e);
		}
	}

	function stopGeolocation() {
		if (geoWatchId != null) {
			navigator.geolocation.clearWatch(geoWatchId);
			geoWatchId = null;
		}
	}

	// Generic Sensor helpers
	function startSensor(handle: SensorHandle, ctorName: string, options: any = { frequency: 60 }) {
		try {
			handle.error = null;
			if (!w?.[ctorName]) {
				handle.supported = false;
				return;
			}
			handle.instance = new w[ctorName](options);
			handle.instance.addEventListener('reading', () => {
				// Populate based on sensor type
				if (ctorName === 'Accelerometer') {
					handle.data = { x: handle.instance.x, y: handle.instance.y, z: handle.instance.z };
				} else if (ctorName === 'Gyroscope') {
					handle.data = { x: handle.instance.x, y: handle.instance.y, z: handle.instance.z };
				} else if (ctorName === 'Magnetometer') {
					handle.data = { x: handle.instance.x, y: handle.instance.y, z: handle.instance.z };
				} else if (ctorName === 'AmbientLightSensor') {
					handle.data = { illuminance: handle.instance.illuminance };
				} else if (ctorName === 'Barometer') {
					handle.data = {
						pressure: handle.instance.pressure,
						temperature: handle.instance?.temperature
					};
				}
			});
			handle.instance.addEventListener('error', (event: any) => {
				handle.error = event?.error?.message || String(event);
			});
			handle.instance.start();
			handle.active = true;
		} catch (e: any) {
			handle.error = e?.message ?? String(e);
			handle.active = false;
		}
	}

	function stopSensor(handle: SensorHandle) {
		try {
			handle.instance?.stop?.();
		} catch {}
		handle.active = false;
	}

	// UI helpers
	function toFixed(n: number | undefined, digits = 2) {
		return typeof n === 'number' && Number.isFinite(n) ? n.toFixed(digits) : '—';
	}

	async function copyJSON(data: unknown) {
		try {
			await navigator.clipboard.writeText(JSON.stringify(data, null, 2));
			alert(m.sensors_copied());
		} catch {}
	}

	async function requestMotionOrientation() {
		try {
			if (motionPermissionAvailable) {
				const res = await (DeviceMotionEvent as any).requestPermission();
				motionPermission = res === 'granted' ? 'granted' : 'denied';
			}
			if (orientationPermissionAvailable) {
				const res2 = await (DeviceOrientationEvent as any).requestPermission();
				orientationPermission = res2 === 'granted' ? 'granted' : 'denied';
			}
		} catch (_) {
			if (motionPermissionAvailable && motionPermission === 'unknown') motionPermission = 'denied';
			if (orientationPermissionAvailable && orientationPermission === 'unknown')
				orientationPermission = 'denied';
		}
	}

	// Kick off light permission checks lazily in UI; starting sensors will request permissions where needed.
</script>

<h2><i class="ti ti-cpu-2"></i> {m.sensors_title()}</h2>

<div class="sections">
	{#if motionPermissionAvailable || orientationPermissionAvailable}
		<section>
			<h3>{m.sensors_permissions()}</h3>
			<div class="row">
				<button onclick={requestMotionOrientation}
					><i class="ti ti-key"></i> {m.sensors_enableMotionOrientation()}</button
				>
			</div>
			<ul class="kv">
				{#if motionPermissionAvailable}
					<li>
						<span class="key">{m.sensors_motion()}</span>
						<span>
							{#if motionPermission === 'granted'}{m.sensors_status_granted()}
							{:else if motionPermission === 'denied'}{m.sensors_status_denied()}
							{:else}{m.sensors_status_unknown()}{/if}
						</span>
					</li>
				{/if}
				{#if orientationPermissionAvailable}
					<li>
						<span class="key">{m.sensors_orientation()}</span>
						<span>
							{#if orientationPermission === 'granted'}{m.sensors_status_granted()}
							{:else if orientationPermission === 'denied'}{m.sensors_status_denied()}
							{:else}{m.sensors_status_unknown()}{/if}
						</span>
					</li>
				{/if}
			</ul>
		</section>
	{/if}
	<section>
		<h3>{m.sensors_geolocation()}</h3>
		{#if geoSupported}
			<div class="row">
				<button onclick={startGeolocation} disabled={geoWatchId !== null}>
					<i class="ti ti-player-play"></i>
					{m.sensors_start()}
				</button>
				<button onclick={stopGeolocation} disabled={geoWatchId === null}>
					<i class="ti ti-player-stop"></i>
					{m.sensors_stop()}
				</button>
			</div>
			{#if geoError}
				<div class="error">{geoError}</div>
			{/if}
			{#if geolocation}
				<ul class="kv">
					<li><span class="key">lat</span><span>{geolocation.coords.latitude}</span></li>
					<li><span class="key">lon</span><span>{geolocation.coords.longitude}</span></li>
					<li>
						<span class="key">{m.sensors_accuracy()}</span><span
							>{toFixed(geolocation.coords.accuracy)}</span
						>
					</li>
					<li>
						<span class="key">{m.sensors_altitude()}</span><span
							>{geolocation.coords.altitude ?? '—'}</span
						>
					</li>
					<li>
						<span class="key">{m.sensors_heading()}</span><span
							>{toFixed(geolocation.coords.heading ?? undefined)}</span
						>
					</li>
					<li>
						<span class="key">{m.sensors_speed()}</span><span
							>{toFixed(geolocation.coords.speed ?? undefined)}</span
						>
					</li>
					<li>
						<span class="key">{m.sensors_timestamp()}</span><span
							>{new Date(geolocation.timestamp).toLocaleString()}</span
						>
					</li>
				</ul>
				<div class="row">
					<button onclick={() => copyJSON(geolocation)}
						><i class="ti ti-copy"></i> {m.sensors_copy()}</button
					>
				</div>
			{/if}
		{:else}
			<div class="subdued">{m.sensors_notSupported()}</div>
		{/if}
	</section>

	<section>
		<h3>{m.sensors_deviceMotion()}</h3>
		{#if motionSupported}
			<ul class="kv">
				<li><span class="key">ax</span><span>{toFixed(deviceMotion?.ax)}</span></li>
				<li><span class="key">ay</span><span>{toFixed(deviceMotion?.ay)}</span></li>
				<li><span class="key">az</span><span>{toFixed(deviceMotion?.az)}</span></li>
				<li><span class="key">α</span><span>{toFixed(deviceMotion?.gx)}</span></li>
				<li><span class="key">β</span><span>{toFixed(deviceMotion?.gy)}</span></li>
				<li><span class="key">γ</span><span>{toFixed(deviceMotion?.gz)}</span></li>
			</ul>
			<div class="row">
				<button onclick={() => copyJSON(deviceMotion)}
					><i class="ti ti-copy"></i> {m.sensors_copy()}</button
				>
			</div>
		{:else}
			<div class="subdued">{m.sensors_notSupported()}</div>
		{/if}
	</section>

	<section>
		<h3>{m.sensors_deviceOrientation()}</h3>
		{#if orientationSupported}
			<ul class="kv">
				<li><span class="key">alpha</span><span>{toFixed(deviceOrientation?.alpha)}</span></li>
				<li><span class="key">beta</span><span>{toFixed(deviceOrientation?.beta)}</span></li>
				<li><span class="key">gamma</span><span>{toFixed(deviceOrientation?.gamma)}</span></li>
			</ul>
			<div class="row">
				<button onclick={() => copyJSON(deviceOrientation)}
					><i class="ti ti-copy"></i> {m.sensors_copy()}</button
				>
			</div>
		{:else}
			<div class="subdued">{m.sensors_notSupported()}</div>
		{/if}
	</section>

	<section>
		<h3>{m.sensors_accelerometer()}</h3>
		{#if accelerometer.supported}
			<div class="row">
				{#if !accelerometer.active}
					<button onclick={() => startSensor(accelerometer, 'Accelerometer')}
						><i class="ti ti-player-play"></i> {m.sensors_start()}</button
					>
				{:else}
					<button onclick={() => stopSensor(accelerometer)}
						><i class="ti ti-player-stop"></i> {m.sensors_stop()}</button
					>
				{/if}
			</div>
			{#if accelerometer.error}
				<div class="error">{accelerometer.error}</div>
			{/if}
			<ul class="kv">
				<li><span class="key">x</span><span>{toFixed(accelerometer.data.x as number)}</span></li>
				<li><span class="key">y</span><span>{toFixed(accelerometer.data.y as number)}</span></li>
				<li><span class="key">z</span><span>{toFixed(accelerometer.data.z as number)}</span></li>
			</ul>
		{:else}
			<div class="subdued">{m.sensors_notSupported()}</div>
		{/if}
	</section>

	<section>
		<h3>{m.sensors_gyroscope()}</h3>
		{#if gyroscope.supported}
			<div class="row">
				{#if !gyroscope.active}
					<button onclick={() => startSensor(gyroscope, 'Gyroscope')}
						><i class="ti ti-player-play"></i> {m.sensors_start()}</button
					>
				{:else}
					<button onclick={() => stopSensor(gyroscope)}
						><i class="ti ti-player-stop"></i> {m.sensors_stop()}</button
					>
				{/if}
			</div>
			{#if gyroscope.error}
				<div class="error">{gyroscope.error}</div>
			{/if}
			<ul class="kv">
				<li><span class="key">x</span><span>{toFixed(gyroscope.data.x as number)}</span></li>
				<li><span class="key">y</span><span>{toFixed(gyroscope.data.y as number)}</span></li>
				<li><span class="key">z</span><span>{toFixed(gyroscope.data.z as number)}</span></li>
			</ul>
		{:else}
			<div class="subdued">{m.sensors_notSupported()}</div>
		{/if}
	</section>

	<section>
		<h3>{m.sensors_magnetometer()}</h3>
		{#if magnetometer.supported}
			<div class="row">
				{#if !magnetometer.active}
					<button onclick={() => startSensor(magnetometer, 'Magnetometer', { frequency: 10 })}
						><i class="ti ti-player-play"></i> {m.sensors_start()}</button
					>
				{:else}
					<button onclick={() => stopSensor(magnetometer)}
						><i class="ti ti-player-stop"></i> {m.sensors_stop()}</button
					>
				{/if}
			</div>
			{#if magnetometer.error}
				<div class="error">{magnetometer.error}</div>
			{/if}
			<ul class="kv">
				<li><span class="key">x</span><span>{toFixed(magnetometer.data.x as number)}</span></li>
				<li><span class="key">y</span><span>{toFixed(magnetometer.data.y as number)}</span></li>
				<li><span class="key">z</span><span>{toFixed(magnetometer.data.z as number)}</span></li>
			</ul>
		{:else}
			<div class="subdued">{m.sensors_notSupported()}</div>
		{/if}
	</section>

	<section>
		<h3>{m.sensors_ambientLight()}</h3>
		{#if ambientLight.supported}
			<div class="row">
				{#if !ambientLight.active}
					<button onclick={() => startSensor(ambientLight, 'AmbientLightSensor')}
						><i class="ti ti-player-play"></i> {m.sensors_start()}</button
					>
				{:else}
					<button onclick={() => stopSensor(ambientLight)}
						><i class="ti ti-player-stop"></i> {m.sensors_stop()}</button
					>
				{/if}
			</div>
			{#if ambientLight.error}
				<div class="error">{ambientLight.error}</div>
			{/if}
			<ul class="kv">
				<li>
					<span class="key">{m.sensors_illuminance()}</span><span
						>{toFixed(ambientLight.data.illuminance as number)}</span
					>
				</li>
			</ul>
		{:else}
			<div class="subdued">{m.sensors_notSupported()}</div>
		{/if}
	</section>

	<section>
		<h3>{m.sensors_barometer()}</h3>
		{#if barometer.supported}
			<div class="row">
				{#if !barometer.active}
					<button onclick={() => startSensor(barometer, 'Barometer')}
						><i class="ti ti-player-play"></i> {m.sensors_start()}</button
					>
				{:else}
					<button onclick={() => stopSensor(barometer)}
						><i class="ti ti-player-stop"></i> {m.sensors_stop()}</button
					>
				{/if}
			</div>
			{#if barometer.error}
				<div class="error">{barometer.error}</div>
			{/if}
			<ul class="kv">
				<li>
					<span class="key">{m.sensors_pressure()}</span><span
						>{toFixed(barometer.data.pressure as number)}</span
					>
				</li>
				<li>
					<span class="key">{m.sensors_temperature()}</span><span
						>{barometer.data.temperature ?? '—'}</span
					>
				</li>
			</ul>
		{:else}
			<div class="subdued">{m.sensors_notSupported()}</div>
		{/if}
	</section>
</div>

<style>
	.sections {
		display: grid;
		grid-template-columns: 1fr 1fr;
		gap: 1rem;
	}

	section {
		border: 1px solid currentColor;
		border-radius: 0.5rem;
		padding: 0.75rem;
	}

	h3 {
		margin: 0 0 0.5rem 0;
		font-size: 1.1rem;
	}

	.row {
		display: flex;
		gap: 0.5rem;
		align-items: center;
		flex-wrap: wrap;
	}

	.kv {
		list-style: none;
		padding: 0;
		margin: 0.5rem 0 0 0;
		display: grid;
		grid-template-columns: auto 1fr;
		gap: 0.25rem 0.75rem;
		align-items: baseline;
	}

	.kv .key {
		opacity: 0.8;
		margin-right: 0.25em;
	}

	.error {
		color: #ff6b6b;
		margin: 0.25rem 0;
	}

	.subdued {
		opacity: 0.7;
	}

	button {
		background: none;
		color: inherit;
		border: 1px solid currentColor;
		border-radius: 0.25rem;
		padding: 0.25rem 0.5rem;
		display: inline-flex;
		align-items: center;
		gap: 0.4rem;
	}
</style>
