/**
 * Multi-source printer discovery: curated ethical shops + OpenStreetMap.
 * Ported from rPubs-online/src/lib/discover-printers.ts.
 */

import { readFileSync } from "node:fs";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";

const __dirname = dirname(fileURLToPath(import.meta.url));
const curatedShopsData = JSON.parse(readFileSync(join(__dirname, "curated-shops.json"), "utf-8"));

export type ProviderSource = "curated" | "discovered";

export interface DiscoveredProvider {
	id: string;
	name: string;
	source: ProviderSource;
	distance_km: number;
	lat: number;
	lng: number;
	city: string;
	address?: string;
	website?: string;
	phone?: string;
	email?: string;
	capabilities?: string[];
	tags?: string[];
	description?: string;
}

interface CuratedShop {
	name: string;
	lat: number;
	lng: number;
	city: string;
	country: string;
	address: string;
	website: string;
	email?: string;
	phone?: string;
	capabilities: string[];
	tags: string[];
	description: string;
	formats?: string[];
}

const CURATED_SHOPS: CuratedShop[] = curatedShopsData as CuratedShop[];

function haversineKm(lat1: number, lng1: number, lat2: number, lng2: number): number {
	const R = 6371;
	const dLat = ((lat2 - lat1) * Math.PI) / 180;
	const dLng = ((lng2 - lng1) * Math.PI) / 180;
	const a =
		Math.sin(dLat / 2) ** 2 +
		Math.cos((lat1 * Math.PI) / 180) *
			Math.cos((lat2 * Math.PI) / 180) *
			Math.sin(dLng / 2) ** 2;
	return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}

function searchCurated(
	lat: number,
	lng: number,
	radiusKm: number,
	formatId?: string,
): DiscoveredProvider[] {
	return CURATED_SHOPS.filter((shop) => {
		const dist = haversineKm(lat, lng, shop.lat, shop.lng);
		if (dist > radiusKm) return false;
		if (formatId && shop.formats && !shop.formats.includes(formatId)) return false;
		return true;
	}).map((shop) => ({
		id: `curated-${shop.name.toLowerCase().replace(/\s+/g, "-")}`,
		name: shop.name,
		source: "curated" as const,
		distance_km: Math.round(haversineKm(lat, lng, shop.lat, shop.lng) * 10) / 10,
		lat: shop.lat,
		lng: shop.lng,
		city: `${shop.city}, ${shop.country}`,
		address: shop.address,
		website: shop.website,
		email: shop.email,
		phone: shop.phone,
		capabilities: shop.capabilities,
		tags: shop.tags,
		description: shop.description,
	}));
}

const OVERPASS_API = "https://overpass-api.de/api/interpreter";

async function searchOSM(
	lat: number,
	lng: number,
	radiusMeters: number,
): Promise<DiscoveredProvider[]> {
	const query = `
[out:json][timeout:10];
(
  nwr["shop"="copyshop"](around:${radiusMeters},${lat},${lng});
  nwr["shop"="printing"](around:${radiusMeters},${lat},${lng});
  nwr["craft"="printer"](around:${radiusMeters},${lat},${lng});
  nwr["office"="printing"](around:${radiusMeters},${lat},${lng});
  nwr["amenity"="copyshop"](around:${radiusMeters},${lat},${lng});
  nwr["shop"="stationery"]["printing"="yes"](around:${radiusMeters},${lat},${lng});
);
out center tags;
`;

	const res = await fetch(OVERPASS_API, {
		method: "POST",
		body: `data=${encodeURIComponent(query)}`,
		headers: {
			"Content-Type": "application/x-www-form-urlencoded",
			"User-Agent": "rPubs/1.0 (rspace.online)",
		},
		signal: AbortSignal.timeout(12000),
	});

	if (!res.ok) return [];

	const data = await res.json();
	const elements: Array<{
		id: number;
		type: string;
		lat?: number;
		lon?: number;
		center?: { lat: number; lon: number };
		tags?: Record<string, string>;
	}> = data.elements || [];

	const seen = new Set<string>();

	return elements
		.filter((el) => {
			const name = el.tags?.name;
			if (!name) return false;
			if (seen.has(name.toLowerCase())) return false;
			seen.add(name.toLowerCase());
			return true;
		})
		.map((el) => {
			const elLat = el.lat ?? el.center?.lat ?? lat;
			const elLng = el.lon ?? el.center?.lon ?? lng;
			const tags = el.tags || {};

			const city = tags["addr:city"] || tags["addr:suburb"] || tags["addr:town"] || "";
			const street = tags["addr:street"] || "";
			const housenumber = tags["addr:housenumber"] || "";
			const address = [housenumber, street, city].filter(Boolean).join(" ").trim();

			const capabilities: string[] = [];
			if (tags["service:copy"] === "yes" || tags.shop === "copyshop") capabilities.push("laser-print");
			if (tags["service:binding"] === "yes") capabilities.push("saddle-stitch", "perfect-bind");
			if (tags["service:print"] === "yes" || tags.shop === "printing") capabilities.push("laser-print");

			return {
				id: `osm-${el.type}-${el.id}`,
				name: tags.name!,
				source: "discovered" as const,
				distance_km: Math.round(haversineKm(lat, lng, elLat, elLng) * 10) / 10,
				lat: elLat,
				lng: elLng,
				city: city || "Nearby",
				address: address || undefined,
				website: tags.website || tags["contact:website"] || undefined,
				phone: tags.phone || tags["contact:phone"] || undefined,
				email: tags.email || tags["contact:email"] || undefined,
				capabilities: capabilities.length > 0 ? capabilities : undefined,
				description: tags.description || undefined,
			};
		})
		.sort((a, b) => a.distance_km - b.distance_km);
}

export interface DiscoverOptions {
	lat: number;
	lng: number;
	radiusKm?: number;
	formatId?: string;
}

export async function discoverPrinters(opts: DiscoverOptions): Promise<DiscoveredProvider[]> {
	const { lat, lng, radiusKm = 100, formatId } = opts;
	const radiusMeters = radiusKm * 1000;

	const [curated, osm] = await Promise.all([
		Promise.resolve(searchCurated(lat, lng, radiusKm, formatId)),
		searchOSM(lat, lng, radiusMeters).catch((err) => {
			console.error("[rpubs] OSM search failed:", err);
			return [] as DiscoveredProvider[];
		}),
	]);

	const curatedNames = new Set(curated.map((p) => p.name.toLowerCase()));
	const filteredOsm = osm.filter((p) => !curatedNames.has(p.name.toLowerCase()));

	let allCurated = curated;
	if (curated.length === 0 && filteredOsm.length < 3) {
		allCurated = searchCurated(lat, lng, 20000, formatId).slice(0, 5);
	}

	const combined = [...allCurated, ...filteredOsm];
	combined.sort((a, b) => a.distance_km - b.distance_km);

	return combined;
}
