import { OwlClient, ValidationError, ApiError } from './owlClient';
import type { ApiStationsNearestStations } from '@owlboard/owlboard-ts';
import { prefs } from './preferences.svelte';

class NearestStationsState {
	list = $state<ApiStationsNearestStations.StationsNearestStations[]>([]);
	currentHash = $state('');
	loading = $state(false);
	error = $state<string | null>(null);

	private watcherId: number | null = null;

	private initGeoConfig: PositionOptions = {
		enableHighAccuracy: false,
		timeout: 500,
		maximumAge: Infinity
	};

	private geoConfig: PositionOptions = {
		enableHighAccuracy: false,
		timeout: 20000,
		maximumAge: 30000
	};

	constructor() {
		if (typeof window === 'undefined' || !('geolocation' in navigator)) return;

		$effect.root(() => {
			$effect(() => {
				if (prefs.current.NearToMe) {
					this.startTracking();
				} else {
					this.stopTracking();
				}
			});
		});
	}

	private startTracking() {
		if (this.watcherId !== null) return; // Already tracking
		this.loading = true;
		this.jumpstart();
		this.initWatcher();
	}

	private stopTracking() {
		if (this.watcherId !== null) {
			navigator.geolocation.clearWatch(this.watcherId);
			this.watcherId = null;
		}
		this.list = [];
		this.currentHash = '';
		this.loading = false;
		this.error = null;
	}

	private jumpstart() {
		navigator.geolocation.getCurrentPosition(
			(pos) => this.handleUpdate(pos.coords.latitude, pos.coords.longitude),
			(err) => this.handleError(err),
			this.initGeoConfig
		);
	}

	private initWatcher() {
		this.watcherId = navigator.geolocation.watchPosition(
			(pos) => this.handleUpdate(pos.coords.latitude, pos.coords.longitude),
			(err) => this.handleError(err),
			this.geoConfig
		);
	}

	private async handleUpdate(lat: number, lon: number) {
		const newHash = OwlClient.stationData.generateGeohash(lat, lon);
		if (newHash !== this.currentHash) {
			this.loading = true;

			try {
				const result = await OwlClient.stationData.getNearestStations(newHash);
				this.list = result.data;
				this.error = null;
				this.currentHash = newHash;
			} catch (e) {
				this.handleApiError(e);
			} finally {
				this.loading = false;
			}
		}
	}

	private handleError(err: GeolocationPositionError) {
		if (err.code === 1) {
			this.error = 'Location access denied by device';
		} else {
			this.error = 'Waiting for GPS signal...';
		}
	}

	private handleApiError(e: unknown) {
		if (e instanceof ValidationError) {
			this.error = `Request Error: ${e.reason} (Field: ${e.field})`;
		} else if (e instanceof ApiError) {
			switch (e.status) {
				case 404:
					this.error = 'No stations found nearby';
					break;
				case 429:
					this.error = 'Too many requests, will retry';
					break;
				case 500:
					this.error = 'Server Error, will retry';
					break;
				default:
					this.error = `Service error: ${e.code}`;
			}
		} else {
			this.error = 'Connection lost, waiting for signal';
		}

		console.error('OwlBoard API Error:', e);
	}
}

export const nearestStationsState = new NearestStationsState();
