import type { GeoPoint, Point2D } from "./types";

const DEG_TO_RAD = Math.PI / 180;
const METERS_PER_DEG = 111_320; // at equator

export interface Projection {
	toSVG(geo: GeoPoint): Point2D;
	toGeo(pt: Point2D): GeoPoint;
	viewBox: string;
}

/**
 * Create an equirectangular projection centered on the centroid of all points.
 * Scales so the bounding box fits ~800 SVG units wide with padding.
 */
export function createProjection(allPoints: GeoPoint[], targetWidth = 800, padding = 40): Projection {
	if (allPoints.length === 0) {
		return { toSVG: () => ({ x: 0, y: 0 }), toGeo: () => ({ lng: 0, lat: 0 }), viewBox: "0 0 800 600" };
	}

	// Centroid
	const refLat = allPoints.reduce((s, p) => s + p.lat, 0) / allPoints.length;
	const refLng = allPoints.reduce((s, p) => s + p.lng, 0) / allPoints.length;
	const cosLat = Math.cos(refLat * DEG_TO_RAD);

	// Project all points to find bounds (meters from centroid)
	const projected = allPoints.map((p) => ({
		x: (p.lng - refLng) * cosLat * METERS_PER_DEG,
		y: -(p.lat - refLat) * METERS_PER_DEG, // SVG y-down
	}));

	let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
	for (const p of projected) {
		if (p.x < minX) minX = p.x;
		if (p.x > maxX) maxX = p.x;
		if (p.y < minY) minY = p.y;
		if (p.y > maxY) maxY = p.y;
	}

	const rangeX = maxX - minX || 1;
	const rangeY = maxY - minY || 1;
	const scale = (targetWidth - 2 * padding) / Math.max(rangeX, rangeY);
	const cx = (minX + maxX) / 2;
	const cy = (minY + maxY) / 2;
	const svgW = rangeX * scale + 2 * padding;
	const svgH = rangeY * scale + 2 * padding;

	function toSVG(geo: GeoPoint): Point2D {
		const mx = (geo.lng - refLng) * cosLat * METERS_PER_DEG;
		const my = -(geo.lat - refLat) * METERS_PER_DEG;
		return {
			x: (mx - cx) * scale + svgW / 2,
			y: (my - cy) * scale + svgH / 2,
		};
	}

	function toGeo(pt: Point2D): GeoPoint {
		const mx = (pt.x - svgW / 2) / scale + cx;
		const my = (pt.y - svgH / 2) / scale + cy;
		return {
			lng: mx / (cosLat * METERS_PER_DEG) + refLng,
			lat: -(my / METERS_PER_DEG) + refLat,
		};
	}

	return {
		toSVG,
		toGeo,
		viewBox: `0 0 ${Math.round(svgW)} ${Math.round(svgH)}`,
	};
}
