import type { CheckInLocationSnapshot } from '@/domain/check-in'
import { isTrustworthyCoordinate } from '@/domain/poi'

interface CurrentLocationOptions {
  highAccuracyExpireTime?: number
  timeoutMs?: number
}

export function isLocationSnapshotFresh(
  capturedAt: string,
  checkedAt = Date.now(),
  maxAgeMs = 5 * 60 * 1000,
): boolean {
  const capturedTimestamp = Date.parse(capturedAt)
  if (!Number.isFinite(capturedTimestamp)
    || !Number.isFinite(checkedAt)
    || !Number.isFinite(maxAgeMs)
    || maxAgeMs < 0) {
    return false
  }

  const ageMs = checkedAt - capturedTimestamp
  return ageMs >= 0 && ageMs <= maxAgeMs
}

function locationOptionsSupported(): boolean {
  try {
    return typeof uni.canIUse !== 'function'
      || uni.canIUse('getLocation.object.isHighAccuracy')
  }
  catch {
    return false
  }
}

export function isLocationPermissionDenied(error: unknown): boolean {
  if (!error || typeof error !== 'object')
    return false
  const message = String((error as { errMsg?: unknown }).errMsg ?? '')
  return !/privacy/i.test(message)
    && /auth deny|auth denied|authorize:fail|permission/i.test(message)
}

export function getCurrentGcj02Location(options: CurrentLocationOptions = {}): Promise<CheckInLocationSnapshot> {
  return new Promise((resolve, reject) => {
    const highAccuracyExpireTime = Math.max(3000, options.highAccuracyExpireTime ?? 8000)
    const timeoutMs = Math.max(3000, options.timeoutMs ?? 15000)
    let settled = false
    let timeout: ReturnType<typeof setTimeout> | null = null
    const finish = (callback: () => void) => {
      if (settled)
        return
      settled = true
      if (timeout)
        clearTimeout(timeout)
      callback()
    }
    timeout = setTimeout(() => {
      finish(() => reject(new Error('定位超时，请到开阔处重试')))
    }, timeoutMs)
    const commonOptions = {
      type: 'gcj02' as const,
      success: (result: UniNamespace.GetLocationSuccess) => {
        const longitude = Number(result.longitude)
        const latitude = Number(result.latitude)
        const accuracy = Number(result.accuracy ?? result.horizontalAccuracy)
        if (!isTrustworthyCoordinate(longitude, latitude)) {
          finish(() => reject(new Error('定位结果无效，请稍后重试')))
          return
        }
        finish(() => resolve({
          longitude,
          latitude,
          accuracy: Number.isFinite(accuracy) && accuracy >= 0 ? accuracy : null,
          receivedAt: new Date().toISOString(),
          coordinateSystem: 'GCJ02',
        }))
      },
      fail: (error: unknown) => finish(() => reject(error)),
    }

    if (locationOptionsSupported()) {
      uni.getLocation({
        ...commonOptions,
        isHighAccuracy: true,
        highAccuracyExpireTime,
      })
      return
    }
    uni.getLocation(commonOptions)
  })
}
