import { inArray } from "drizzle-orm";
import { db } from "@/db";
import { brigades } from "@/db/schema";
import { haversineMeters } from "./haversine";
import { geoConfig } from "./config";
import type { Coordinates, GeoCandidate } from "./types";

/**
 * REINE Vorfilter-Logik (ohne DB): reichert Treffer mit Koordinaten an,
 * sortiert die innerhalb des Radius nach vorne, schiebt Wehren außerhalb des
 * Radius sowie ohne Koordinaten ans Ende und kappt hart bei `maxCandidates`,
 * um URL-Länge und Latenz des OSRM-`/table`-Aufrufs zu begrenzen.
 *
 * Ausgelagert, damit die Logik ohne laufendes Postgres testbar ist.
 */
export function filterAndCapCandidates<T extends { brigadeId: string }>(
  origin: Coordinates,
  hits: T[],
  coordsFor: (brigadeId: string) => Coordinates | null,
  radiusKm: number,
  maxCandidates: number,
): (T & GeoCandidate)[] {
  if (hits.length === 0) return [];
  const enriched = hits.map((h) => ({
    ...h,
    brigadeCoords: coordsFor(h.brigadeId),
  }));

  const radiusMeters = radiusKm * 1000;
  const within: (T & GeoCandidate)[] = [];
  const rest: (T & GeoCandidate)[] = [];
  for (const e of enriched) {
    if (e.brigadeCoords && haversineMeters(origin, e.brigadeCoords) <= radiusMeters) {
      within.push(e);
    } else {
      rest.push(e);
    }
  }
  return [...within, ...rest].slice(0, maxCandidates);
}

/**
 * Adapter zwischen Suche und Geo: lädt `brigades.lat/lng` für die getroffenen
 * Wehren und filtert grob per Radius vor dem OSRM-Call. IDs sind uuid (string).
 */
export async function searchHitsToGeoCandidates<T extends { brigadeId: string }>(
  origin: Coordinates,
  hits: T[],
  radiusKm = geoConfig.defaultRadiusKm,
  maxCandidates = geoConfig.maxCandidates,
): Promise<(T & GeoCandidate)[]> {
  const ids = [...new Set(hits.map((h) => h.brigadeId))];
  if (ids.length === 0) return [];

  const rows = await db
    .select({ id: brigades.id, lat: brigades.lat, lng: brigades.lng })
    .from(brigades)
    .where(inArray(brigades.id, ids));

  const coordsById = new Map<string, Coordinates | null>(
    rows.map((r) => [
      r.id,
      r.lat != null && r.lng != null ? { lat: r.lat, lng: r.lng } : null,
    ]),
  );

  return filterAndCapCandidates(
    origin,
    hits,
    (id) => coordsById.get(id) ?? null,
    radiusKm,
    maxCandidates,
  );
}
