/**
 * 지리 좌표 관련 유틸리티 함수
 */

/**
 * Haversine 공식을 사용하여 두 좌표 간의 거리 계산 (km)
 * 
 * @param lat1 - 첫 번째 지점의 위도
 * @param lon1 - 첫 번째 지점의 경도
 * @param lat2 - 두 번째 지점의 위도
 * @param lon2 - 두 번째 지점의 경도
 * @returns 두 지점 간의 거리 (km)
 */
export function calculateDistance(
  lat1: number,
  lon1: number,
  lat2: number,
  lon2: number
): number {
  const R = 6371; // 지구 반경 (km)
  
  const dLat = toRadians(lat2 - lat1);
  const dLon = toRadians(lon2 - lon1);
  
  const a =
    Math.sin(dLat / 2) * Math.sin(dLat / 2) +
    Math.cos(toRadians(lat1)) *
      Math.cos(toRadians(lat2)) *
      Math.sin(dLon / 2) *
      Math.sin(dLon / 2);
  
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  
  return R * c;
}

/**
 * 각도를 라디안으로 변환
 */
function toRadians(degrees: number): number {
  return degrees * (Math.PI / 180);
}

/**
 * 라디안을 각도로 변환
 */
export function toDegrees(radians: number): number {
  return radians * (180 / Math.PI);
}

/**
 * 좌표 배열에서 총 거리 계산
 * 
 * @param coordinates - { latitude, longitude }[] 형태의 좌표 배열
 * @returns 총 거리 (km)
 */
export function calculateTotalDistance(
  coordinates: Array<{ latitude: number; longitude: number }>
): number {
  let totalDistance = 0;
  
  for (let i = 1; i < coordinates.length; i++) {
    const prev = coordinates[i - 1];
    const curr = coordinates[i];
    totalDistance += calculateDistance(
      prev.latitude,
      prev.longitude,
      curr.latitude,
      curr.longitude
    );
  }
  
  return totalDistance;
}

/**
 * 좌표가 특정 반경 내에 있는지 확인
 * 
 * @param centerLat - 중심점 위도
 * @param centerLon - 중심점 경도
 * @param pointLat - 확인할 지점의 위도
 * @param pointLon - 확인할 지점의 경도
 * @param radiusKm - 반경 (km)
 * @returns 반경 내에 있으면 true
 */
export function isWithinRadius(
  centerLat: number,
  centerLon: number,
  pointLat: number,
  pointLon: number,
  radiusKm: number
): boolean {
  const distance = calculateDistance(centerLat, centerLon, pointLat, pointLon);
  return distance <= radiusKm;
}

/**
 * 두 좌표 사이의 방위각(bearing) 계산
 * 
 * @param lat1 - 시작점 위도
 * @param lon1 - 시작점 경도
 * @param lat2 - 도착점 위도
 * @param lon2 - 도착점 경도
 * @returns 방위각 (0-360도)
 */
export function calculateBearing(
  lat1: number,
  lon1: number,
  lat2: number,
  lon2: number
): number {
  const dLon = toRadians(lon2 - lon1);
  const lat1Rad = toRadians(lat1);
  const lat2Rad = toRadians(lat2);
  
  const x = Math.sin(dLon) * Math.cos(lat2Rad);
  const y =
    Math.cos(lat1Rad) * Math.sin(lat2Rad) -
    Math.sin(lat1Rad) * Math.cos(lat2Rad) * Math.cos(dLon);
  
  let bearing = toDegrees(Math.atan2(x, y));
  bearing = (bearing + 360) % 360; // 0-360 범위로 정규화
  
  return bearing;
}

/**
 * 좌표 배열의 경계 상자(bounding box) 계산
 * 
 * @param coordinates - 좌표 배열
 * @returns { minLat, maxLat, minLon, maxLon }
 */
export function getBoundingBox(
  coordinates: Array<{ latitude: number; longitude: number }>
): { minLat: number; maxLat: number; minLon: number; maxLon: number } | null {
  if (coordinates.length === 0) return null;
  
  let minLat = coordinates[0].latitude;
  let maxLat = coordinates[0].latitude;
  let minLon = coordinates[0].longitude;
  let maxLon = coordinates[0].longitude;
  
  for (const coord of coordinates) {
    minLat = Math.min(minLat, coord.latitude);
    maxLat = Math.max(maxLat, coord.latitude);
    minLon = Math.min(minLon, coord.longitude);
    maxLon = Math.max(maxLon, coord.longitude);
  }
  
  return { minLat, maxLat, minLon, maxLon };
}

/**
 * 좌표 배열의 중심점 계산
 * 
 * @param coordinates - 좌표 배열
 * @returns { latitude, longitude } 중심점
 */
export function getCenterPoint(
  coordinates: Array<{ latitude: number; longitude: number }>
): { latitude: number; longitude: number } | null {
  if (coordinates.length === 0) return null;
  
  let sumLat = 0;
  let sumLon = 0;
  
  for (const coord of coordinates) {
    sumLat += coord.latitude;
    sumLon += coord.longitude;
  }
  
  return {
    latitude: sumLat / coordinates.length,
    longitude: sumLon / coordinates.length,
  };
}
