import "server-only";

// Free geocoding via OpenStreetMap's Nominatim service — no API key needed.
// Usage policy (https://operations.osmfoundation.org/policies/nominatim/)
// requires a descriptive User-Agent and reasonable request volume, which is
// fine here since this only runs when an admin saves a new business address.
const USER_AGENT = "hair-by-nyasia-salon-site (contact page map)";

// Nominatim's index rarely covers suite/unit-level detail, so a full address
// like "259 Broad St Ste 110, Matawan, NJ 07747" returns zero results even
// though the base street address geocodes fine. If the first attempt comes
// up empty, retry with the unit/suite portion stripped out.
const UNIT_PATTERN =
  /,?\s*\b(?:suite|ste|unit|apt|apartment|floor|fl|room|rm|bldg|building)\.?\s*#?\s*[\w-]+\.?|,?\s*#\s*[\w-]+/gi;

function stripUnitDesignator(address: string): string | null {
  const stripped = address
    .replace(UNIT_PATTERN, "")
    .replace(/\s{2,}/g, " ")
    .replace(/,\s*,/g, ",")
    .replace(/,\s*$/, "")
    .trim();
  return stripped && stripped !== address.trim() ? stripped : null;
}

async function lookup(query: string): Promise<{ lat: number; lng: number } | null> {
  const url = `https://nominatim.openstreetmap.org/search?format=json&limit=1&q=${encodeURIComponent(
    query
  )}`;
  const res = await fetch(url, {
    headers: { "User-Agent": USER_AGENT },
    cache: "no-store",
  });
  if (!res.ok) return null;

  const results = (await res.json()) as Array<{ lat: string; lon: string }>;
  const first = results[0];
  if (!first) return null;

  const lat = Number(first.lat);
  const lng = Number(first.lon);
  if (!Number.isFinite(lat) || !Number.isFinite(lng)) return null;

  return { lat, lng };
}

export async function geocodeAddress(
  address: string
): Promise<{ lat: number; lng: number } | null> {
  const trimmed = address.trim();
  if (!trimmed) return null;

  try {
    const direct = await lookup(trimmed);
    if (direct) return direct;

    const withoutUnit = stripUnitDesignator(trimmed);
    if (withoutUnit) return await lookup(withoutUnit);

    return null;
  } catch {
    return null;
  }
}
