import { MAP_MIN_ZOOM } from './consts';

export interface GeoBounds {
  south: number;
  west: number;
  north: number;
  east: number;
}

/**
 * Nominal viewport used to derive a zoom from a bounding box. The map only
 * exposes flyTo(lat, lng, zoom), so we approximate fitBounds; being half a
 * zoom level off for unusual window sizes is fine for "show me the matches".
 */
const NOMINAL_VIEWPORT = { width: 1000, height: 700 };
const TILE_SIZE = 512;
/** Keep matches comfortably inside the viewport edges. */
const ZOOM_PADDING = 0.4;
const MAX_FIT_ZOOM = 13;

function mercatorY(lat: number): number {
  const rad = (lat * Math.PI) / 180;
  return Math.log(Math.tan(Math.PI / 4 + rad / 2));
}

/** Convert a bounding box into a flyTo target that roughly fits it on screen. */
export function boundsToCenterZoom(bounds: GeoBounds): { lat: number; lng: number; zoom: number } {
  const south = Math.min(bounds.south, bounds.north);
  const north = Math.max(bounds.south, bounds.north);
  const west = Math.min(bounds.west, bounds.east);
  const east = Math.max(bounds.west, bounds.east);

  const lonSpan = Math.max(east - west, 1e-6);
  const mercSpan = Math.max(mercatorY(north) - mercatorY(south), 1e-6);

  const zoomX = Math.log2((NOMINAL_VIEWPORT.width * 360) / (TILE_SIZE * lonSpan));
  const zoomY = Math.log2((NOMINAL_VIEWPORT.height * 2 * Math.PI) / (TILE_SIZE * mercSpan));
  const zoom = Math.max(
    MAP_MIN_ZOOM,
    Math.min(MAX_FIT_ZOOM, Math.min(zoomX, zoomY) - ZOOM_PADDING)
  );

  return {
    lat: (south + north) / 2,
    lng: (west + east) / 2,
    zoom,
  };
}
