import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { resolveGoogleMapsApiKey } from '../config/runtime-config';
import {
  decodeGoogleEncodedPolyline,
  encodePipePolyline,
  type LatLng,
} from './map-matching.util';

export type DrivingRouteResult = {
  polylinePoints: LatLng[];
  polylineEncoded: string;
  distanceMeters: number;
  distanceKm: number;
  durationSeconds: number;
  routeIndex: number;
  engine: 'google_directions';
};

interface DirectionsApiResponse {
  status?: string;
  error_message?: string;
  routes?: Array<{
    overview_polyline?: { points?: string };
    legs?: Array<{
      distance?: { value?: number };
      duration?: { value?: number };
    }>;
  }>;
}

@Injectable()
export class GoogleDirectionsClient {

  constructor(private readonly config: ConfigService) {}

  hasApiKey(): boolean {
    return !!resolveGoogleMapsApiKey(this.config);
  }

  /**
   * Driving route(s) between two points via Google Directions API.
   * Returns [] when no API key or on hard failure.
   */
  async fetchDrivingRoutes(opts: {
    origin: LatLng;
    destination: LatLng;
    alternatives?: boolean;
  }): Promise<DrivingRouteResult[]> {
    const key = resolveGoogleMapsApiKey(this.config);
    if (!key) {
      return [];
    }

    const params = new URLSearchParams({
      origin: `${opts.origin.lat},${opts.origin.lng}`,
      destination: `${opts.destination.lat},${opts.destination.lng}`,
      mode: 'driving',
      alternatives: opts.alternatives === false ? 'false' : 'true',
      key,
    });

    const url = `https://maps.googleapis.com/maps/api/directions/json?${params}`;

    try {
      const res = await fetch(url);
      if (!res.ok) {
        const body = await res.text();
        return [];
      }

      const data = (await res.json()) as DirectionsApiResponse;
      if (data.status !== 'OK' || !data.routes?.length) {
        return [];
      }

      const out: DrivingRouteResult[] = [];
      for (let i = 0; i < data.routes.length; i++) {
        const r = data.routes[i];
        const encoded = r.overview_polyline?.points;
        if (!encoded) continue;
        const pts = decodeGoogleEncodedPolyline(encoded);
        if (pts.length < 2) continue;

        let totalMeters = 0;
        let totalSeconds = 0;
        for (const leg of r.legs ?? []) {
          if (typeof leg.distance?.value === 'number') {
            totalMeters += leg.distance.value;
          }
          if (typeof leg.duration?.value === 'number') {
            totalSeconds += leg.duration.value;
          }
        }

        out.push({
          polylinePoints: pts,
          polylineEncoded: encodePipePolyline(pts),
          distanceMeters: totalMeters,
          distanceKm: Math.round((totalMeters / 1000) * 1000) / 1000,
          durationSeconds: totalSeconds,
          routeIndex: i,
          engine: 'google_directions',
        });
      }
      return out;
    } catch (err) {
      return [];
    }
  }
}
