/**
 * 浏览器环境 Bridge 提供者
 *
 * 作为普通浏览器中的降级实现，利用 Web API 尽可能模拟原生能力。
 * 当 navigator.geolocation 不可用时，getLocation 会抛出清晰的错误。
 *
 * @module bridge/providers/browser
 */

import type {
  IBridge,
  GetLocationOptions,
  LocationResult,
  ScanCodeOptions,
  ScanCodeResult,
  OpenMapParams,
  ChooseImageOptions,
  ChooseImageResult,
  DeviceInfo,
} from '../types'

/**
 * 浏览器降级提供者
 *
 * 实现了 IBridge 接口的六个方法：
 * - getLocation —— 依赖 navigator.geolocation
 * - scanCode —— 浏览器无法扫码，直接抛出错误
 * - openMap —— 通过 URL Scheme 跳转高德/百度地图
 * - chooseImage —— 通过 <input type="file"> 选择图片
 * - getDeviceInfo —— 通过 navigator / screen 收集设备信息
 * - setTitle —— 通过 document.title 设置标题
 */
const browserProvider: IBridge = {
  // ────────────────────────────────────────────
  // 获取地理位置
  // ────────────────────────────────────────────
  async getLocation(options: GetLocationOptions = {}): Promise<LocationResult> {
    const geolocation = navigator.geolocation

    if (!geolocation) {
      throw new Error('浏览器不支持定位功能（geolocation API 不可用）')
    }

    const {
      enableHighAccuracy = false,
      timeout = 10000,
      needAddress = false,
    } = options

    return new Promise<LocationResult>((resolve, reject) => {
      geolocation.getCurrentPosition(
        (position) => {
          const result: LocationResult = {
            latitude: position.coords.latitude,
            longitude: position.coords.longitude,
            speed: position.coords.speed ?? undefined,
            accuracy: position.coords.accuracy,
          }

          if (needAddress) {
            // 浏览器原生 geolocation 不支持逆地理编码，
            // address 留空，由调用方自行调用第三方服务补齐
            result.address = undefined
          }

          resolve(result)
        },
        (error) => {
          let message: string
          switch (error.code) {
            case error.PERMISSION_DENIED:
              message = '用户拒绝定位授权'
              break
            case error.POSITION_UNAVAILABLE:
              message = '无法获取位置信息'
              break
            case error.TIMEOUT:
              message = '定位请求超时'
              break
            default:
              message = `定位失败: ${error.message}`
          }
          reject(new Error(message))
        },
        {
          enableHighAccuracy,
          timeout,
          maximumAge: 0,
        },
      )
    })
  },

  // ────────────────────────────────────────────
  // 扫码
  // ────────────────────────────────────────────
  async scanCode(_options?: ScanCodeOptions): Promise<ScanCodeResult> {
    throw new Error('浏览器不支持扫码功能，请使用移动端打开')
  },

  // ────────────────────────────────────────────
  // 打开地图
  // ────────────────────────────────────────────
  async openMap(params: OpenMapParams): Promise<boolean> {
    const { latitude, longitude, name, address, scale = 16 } = params

    const label = encodeURIComponent(name || address || '目标位置')

    // 优先尝试高德地图 URI
    // 格式参考: https://lbs.amap.com/api/uri-api/guide/mobile-web/open
    const amapUrl = `https://uri.amap.com/marker?position=${longitude},${latitude}&name=${label}&callnative=1`

    // 同时准备通用经纬度链接作为降级（打开系统默认地图）
    const fallbackUrl = `https://maps.google.com/maps?q=${latitude},${longitude}(${label})&z=${scale}`

    // 检测当前环境
    const ua = navigator.userAgent
    let mapUrl: string

    if (/iPhone|iPad|iPod/i.test(ua)) {
      // iOS：使用 Apple Maps URL Scheme
      mapUrl = `https://maps.apple.com/?q=${label}&ll=${latitude},${longitude}&z=${scale}`
    } else if (/Android/i.test(ua)) {
      // Android：优先使用高德，浏览器会自动唤起
      mapUrl = amapUrl
    } else {
      // 桌面端：打开 Google Maps
      mapUrl = fallbackUrl
    }

    try {
      window.open(mapUrl, '_blank')
      return true
    } catch {
      return false
    }
  },

  // ────────────────────────────────────────────
  // 选择图片
  // ────────────────────────────────────────────
  async chooseImage(options: ChooseImageOptions = {}): Promise<ChooseImageResult> {
    const { count = 1, sourceType = ['album', 'camera'], compress = true } = options

    return new Promise<ChooseImageResult>((resolve, reject) => {
      const input = document.createElement('input')
      input.type = 'file'
      input.accept = 'image/*'

      // 根据 sourceType 设置 capture 属性
      if (sourceType.length === 1 && sourceType[0] === 'camera') {
        input.capture = 'environment'
      }

      // 支持多选
      if (count > 1) {
        input.multiple = true
      }

      // 压缩由 accept / 浏览器默认行为处理，
      // 真实压缩需后续通过 Canvas 实现，此处仅标记

      // 超时兜底：30s 后自动 reject
      const timeoutId = setTimeout(() => {
        reject(new Error('选择图片超时，请重试'))
        cleanup()
      }, 30000)

      const cleanup = () => {
        clearTimeout(timeoutId)
        input.removeEventListener('change', onChange)
        input.removeEventListener('cancel', onCancel)
        input.remove()
      }

      const onChange = () => {
        clearTimeout(timeoutId)

        const files = input.files
        if (!files || files.length === 0) {
          reject(new Error('未选择任何图片'))
          cleanup()
          return
        }

        const tempFilePaths: string[] = []
        const tempFiles: Array<{ path: string; size: number }> = []

        const validFiles: File[] = []
        for (let i = 0; i < files.length; i++) {
          const file = files.item(i)
          if (file) {
            validFiles.push(file)
          }
        }

        for (const file of validFiles) {
          // 通过 URL.createObjectURL 创建临时可访问路径
          const objectUrl = URL.createObjectURL(file)
          tempFilePaths.push(objectUrl)
          tempFiles.push({ path: objectUrl, size: file.size })
        }

        resolve({
          tempFilePaths,
          tempFiles,
        })
        cleanup()
      }

      // 用户取消选择（部分浏览器支持）
      const onCancel = () => {
        clearTimeout(timeoutId)
        reject(new Error('用户取消选择图片'))
        cleanup()
      }

      input.addEventListener('change', onChange)
      // 监听 cancel 事件（部分浏览器）
      input.addEventListener('cancel', onCancel)

      // 兼容移动端 webview 的取消操作：
      // 通过 focus 丢失检测取消操作
      let focusLost = false
      const onFocus = () => {
        focusLost = false
      }
      const onBlur = () => {
        focusLost = true
        // 延迟检测：如果 blur 后 change 未触发，视为取消
        setTimeout(() => {
          if (focusLost && input.files && input.files.length === 0) {
            clearTimeout(timeoutId)
            reject(new Error('用户取消选择图片'))
            cleanup()
          }
        }, 500)
      }

      input.addEventListener('focus', onFocus)
      input.addEventListener('blur', onBlur)

      // 触发文件选择
      input.click()
    })
  },

  // ────────────────────────────────────────────
  // 获取设备信息
  // ────────────────────────────────────────────
  async getDeviceInfo(): Promise<DeviceInfo> {
    const ua = navigator.userAgent

    // 解析平台
    let platform: string
    if (/iPhone|iPad|iPod/i.test(ua)) {
      platform = 'ios'
    } else if (/Android/i.test(ua)) {
      platform = 'android'
    } else if (/Windows/i.test(ua)) {
      platform = 'windows'
    } else if (/Macintosh|Mac OS/i.test(ua)) {
      platform = 'mac'
    } else if (/Linux/i.test(ua)) {
      platform = 'linux'
    } else {
      platform = 'unknown'
    }

    // 尝试解析设备型号（粗糙方式）
    let model = 'unknown'
    const deviceMatch = ua.match(/\(([^)]+)\)/)
    if (deviceMatch && deviceMatch[1]) {
      // 从 UA 括号中取设备信息
      const parts = deviceMatch[1].split(';')
      // 最后一个不含版本号的 token 通常是设备型号
      for (let i = parts.length - 1; i >= 0; i--) {
        const part = parts[i].trim()
        if (
          part &&
          !/AppleWebKit|KHTML|Gecko|Chrome|Safari|Firefox|Edge|Version|Mobile/i.test(part) &&
          !/^[0-9._]+$/.test(part) &&
          !/U;|CPU|iPhone|iPad|iPod|Mac OS|Windows|Android|Linux/i.test(part)
        ) {
          model = part
          break
        }
      }
    }

    // 解析版本
    let version = 'unknown'
    if (platform === 'android') {
      const match = ua.match(/Android\s+([0-9.]+)/i)
      if (match && match[1]) {
        version = match[1]
      }
    } else if (platform === 'ios') {
      const match = ua.match(/OS\s+([0-9_]+)/i)
      if (match && match[1]) {
        version = match[1].replace(/_/g, '.')
      }
    }

    return {
      platform,
      model,
      version,
      language: navigator.language,
      pixelRatio: window.devicePixelRatio || 1,
      screenWidth: screen.width,
      screenHeight: screen.height,
      windowWidth: window.innerWidth,
      windowHeight: window.innerHeight,
    }
  },

  // ────────────────────────────────────────────
  // 设置标题
  // ────────────────────────────────────────────
  async setTitle(title: string): Promise<boolean> {
    document.title = title
    return true
  },
}

export default browserProvider
