/**
 * Fogbreak Location Service
 * GPS-powered showing routes, nearby listings, arrival detection.
 */

import * as Location from 'expo-location';
import { Platform, Linking } from 'react-native';

export interface Coordinates {
  latitude: number;
  longitude: number;
}

export interface ShowingStop {
  id: number;
  address: string;
  latitude: number;
  longitude: number;
  showing_time: string;
  instructions: string;
  lockbox_code: string;
}

const ARRIVAL_RADIUS_METERS = 100;

export async function requestLocationPermission(): Promise<boolean> {
  const { status } = await Location.requestForegroundPermissionsAsync();
  return status === 'granted';
}

export async function requestBackgroundPermission(): Promise<boolean> {
  const { status } = await Location.requestBackgroundPermissionsAsync();
  return status === 'granted';
}

export async function getCurrentLocation(): Promise<Coordinates | null> {
  try {
    const hasPermission = await requestLocationPermission();
    if (!hasPermission) return null;

    const location = await Location.getCurrentPositionAsync({
      accuracy: Location.Accuracy.High,
    });

    return {
      latitude: location.coords.latitude,
      longitude: location.coords.longitude,
    };
  } catch {
    return null;
  }
}

export function calculateDistance(
  from: Coordinates,
  to: Coordinates
): number {
  const R = 3959; // Earth radius in miles
  const dLat = toRad(to.latitude - from.latitude);
  const dLon = toRad(to.longitude - from.longitude);
  const a =
    Math.sin(dLat / 2) * Math.sin(dLat / 2) +
    Math.cos(toRad(from.latitude)) *
      Math.cos(toRad(to.latitude)) *
      Math.sin(dLon / 2) *
      Math.sin(dLon / 2);
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  return R * c;
}

function toRad(deg: number): number {
  return deg * (Math.PI / 180);
}

export function isAtLocation(
  current: Coordinates,
  target: Coordinates,
  radiusMeters: number = ARRIVAL_RADIUS_METERS
): boolean {
  const distanceMiles = calculateDistance(current, target);
  const distanceMeters = distanceMiles * 1609.34;
  return distanceMeters <= radiusMeters;
}

export function estimateDriveTime(distanceMiles: number): number {
  // Rough estimate: 25 mph average for residential/suburban driving
  const hours = distanceMiles / 25;
  return Math.ceil(hours * 60); // Return minutes
}

export function optimizeShowingRoute(
  currentLocation: Coordinates,
  stops: ShowingStop[]
): ShowingStop[] {
  if (stops.length <= 1) return stops;

  // Nearest-neighbor greedy route optimization
  const optimized: ShowingStop[] = [];
  const remaining = [...stops];
  let current: Coordinates = currentLocation;

  while (remaining.length > 0) {
    let nearestIdx = 0;
    let nearestDist = Infinity;

    for (let i = 0; i < remaining.length; i++) {
      const dist = calculateDistance(current, {
        latitude: remaining[i].latitude,
        longitude: remaining[i].longitude,
      });
      if (dist < nearestDist) {
        nearestDist = dist;
        nearestIdx = i;
      }
    }

    const nearest = remaining.splice(nearestIdx, 1)[0];
    optimized.push(nearest);
    current = { latitude: nearest.latitude, longitude: nearest.longitude };
  }

  return optimized;
}

export function openNavigationApp(destination: Coordinates, label?: string): void {
  const { latitude, longitude } = destination;
  const encodedLabel = encodeURIComponent(label ?? `${latitude},${longitude}`);

  if (Platform.OS === 'ios') {
    Linking.openURL(
      `maps:0,0?q=${encodedLabel}&ll=${latitude},${longitude}`
    );
  } else {
    Linking.openURL(
      `geo:0,0?q=${latitude},${longitude}(${encodedLabel})`
    );
  }
}

export function openMultiStopNavigation(stops: ShowingStop[]): void {
  if (stops.length === 0) return;

  if (Platform.OS === 'ios') {
    // Apple Maps supports waypoints via URL
    const waypoints = stops
      .map((s) => `${s.latitude},${s.longitude}`)
      .join('/');
    Linking.openURL(`maps://?daddr=${waypoints}`);
  } else {
    // Google Maps multi-stop
    const origin = stops[0];
    const dest = stops[stops.length - 1];
    const waypointsStr = stops
      .slice(1, -1)
      .map((s) => `${s.latitude},${s.longitude}`)
      .join('|');
    const url = `google.navigation:q=${dest.latitude},${dest.longitude}&waypoints=${waypointsStr}`;
    Linking.openURL(url).catch(() => {
      // Fallback to browser Google Maps
      const gmapsUrl = `https://www.google.com/maps/dir/${origin.latitude},${origin.longitude}/${stops.map((s) => `${s.latitude},${s.longitude}`).join('/')}`;
      Linking.openURL(gmapsUrl);
    });
  }
}

export async function watchPosition(
  callback: (coords: Coordinates) => void,
  intervalMs: number = 5000
): Promise<Location.LocationSubscription | null> {
  const hasPermission = await requestLocationPermission();
  if (!hasPermission) return null;

  return Location.watchPositionAsync(
    {
      accuracy: Location.Accuracy.High,
      timeInterval: intervalMs,
      distanceInterval: 10, // meters
    },
    (location) => {
      callback({
        latitude: location.coords.latitude,
        longitude: location.coords.longitude,
      });
    }
  );
}

export function getRouteStats(
  currentLocation: Coordinates,
  stops: ShowingStop[]
): {
  totalMiles: number;
  totalMinutes: number;
  legs: { from: string; to: string; miles: number; minutes: number }[];
} {
  const legs: { from: string; to: string; miles: number; minutes: number }[] = [];
  let totalMiles = 0;
  let prev: Coordinates = currentLocation;
  let prevLabel = 'Current Location';

  for (const stop of stops) {
    const dest: Coordinates = { latitude: stop.latitude, longitude: stop.longitude };
    const miles = calculateDistance(prev, dest);
    const minutes = estimateDriveTime(miles);
    legs.push({ from: prevLabel, to: stop.address, miles, minutes });
    totalMiles += miles;
    prev = dest;
    prevLabel = stop.address;
  }

  return {
    totalMiles,
    totalMinutes: legs.reduce((sum, leg) => sum + leg.minutes, 0),
    legs,
  };
}
