import { PipelineStage } from "mongoose";
import GasStationModel from "../models/GasStationModel";
import { gasStationApprovedForAppFilter } from "../utils/gasStationVisibility";
import { calculateDistanceHaversine } from "../utils/tripCalculations";

/**
 * Approximate distance (km) from point P to line segment A→B on a local
 * equirectangular plane (good enough for corridor buffers of a few km).
 */
function distancePointToSegmentKm(
  pLat: number,
  pLng: number,
  aLat: number,
  aLng: number,
  bLat: number,
  bLng: number,
): number {
  const toRad = Math.PI / 180;
  const meanLat = ((aLat + bLat) / 2) * toRad;
  const cosLat = Math.cos(meanLat);
  const R = 6371;

  const ax = aLng * toRad * cosLat * R;
  const ay = aLat * toRad * R;
  const bx = bLng * toRad * cosLat * R;
  const by = bLat * toRad * R;
  const px = pLng * toRad * cosLat * R;
  const py = pLat * toRad * R;

  const dx = bx - ax;
  const dy = by - ay;
  const lenSq = dx * dx + dy * dy;

  if (lenSq < 1e-12) {
    return calculateDistanceHaversine(pLat, pLng, aLat, aLng);
  }

  let t = ((px - ax) * dx + (py - ay) * dy) / lenSq;
  t = Math.max(0, Math.min(1, t));
  const cx = ax + t * dx;
  const cy = ay + t * dy;
  const dist = Math.sqrt((px - cx) ** 2 + (py - cy) ** 2);
  return Math.round(dist * 100) / 100;
}

export async function findGasStationsAlongRoute(opts: {
  startLat: number;
  startLng: number;
  endLat: number;
  endLng: number;
  bufferKm: number;
}): Promise<Record<string, unknown>[]> {
  const { startLat, startLng, endLat, endLng, bufferKm } = opts;

  const routeKm = calculateDistanceHaversine(
    startLat,
    startLng,
    endLat,
    endLng,
  );
  // Cover the route with a circle around the midpoint (route/2 + buffer + slack).
  const midLat = (startLat + endLat) / 2;
  const midLng = (startLng + endLng) / 2;
  const maxDistanceMeters = Math.max(
    (routeKm / 2 + bufferKm + 1) * 1000,
    bufferKm * 1000,
  );

  const pipeline: PipelineStage[] = [
    {
      $geoNear: {
        near: { type: "Point", coordinates: [midLng, midLat] },
        distanceField: "distFromMidMeters",
        maxDistance: maxDistanceMeters,
        spherical: true,
        query: gasStationApprovedForAppFilter(),
      },
    },
    { $limit: 200 },
  ];

  const candidates = await GasStationModel.aggregate(pipeline);

  return candidates
    .map((station) => {
      const coords = station.location?.coordinates as
        | [number, number]
        | undefined;
      if (!coords || coords.length < 2) return null;
      const lng = coords[0];
      const lat = coords[1];
      const corridorKm = distancePointToSegmentKm(
        lat,
        lng,
        startLat,
        startLng,
        endLat,
        endLng,
      );
      if (corridorKm > bufferKm) return null;

      return {
        _id: station._id,
        name: station.name,
        address: station.address ?? "",
        city: station.city ?? "",
        state: station.state ?? "",
        location: { lat, lng },
        distanceToRouteKm: corridorKm,
        fuelTypes: station.fuelTypes ?? [],
        mainImage: station.mainImage ?? null,
        logoUrl: station.logoUrl ?? null,
      };
    })
    .filter((s): s is NonNullable<typeof s> => s !== null)
    .sort(
      (a, b) =>
        (a.distanceToRouteKm as number) - (b.distanceToRouteKm as number),
    );
}
