import { DEFAULT_MOCK_LOCATION_BRIDGE_URL, MockLocationBridge } from './mockLocationBridge'
import { MockLocationSource } from './mockLocationSource'
import { RealLocationSource } from './realLocationSource'
import { type LocationSample, type LocationSourceCallbacks, type LocationSourceMode } from './locationSource'

export interface LocationUpdate extends LocationSample {}

export interface LocationControllerDebugState {
  sourceMode: LocationSourceMode
  sourceModeText: string
  listening: boolean
  mockBridgeConnected: boolean
  mockBridgeStatusText: string
  mockBridgeUrlText: string
  mockChannelIdText: string
  mockCoordText: string
  mockSpeedText: string
}

export interface LocationControllerCallbacks {
  onLocation: (update: LocationUpdate) => void
  onStatus: (message: string) => void
  onError: (message: string) => void
  onDebugStateChange?: (state: LocationControllerDebugState) => void
}

function formatSourceModeText(mode: LocationSourceMode): string {
  return mode === 'mock' ? '模拟定位' : '真实定位'
}

function formatMockCoordText(sample: LocationSample | null): string {
  if (!sample) {
    return '--'
  }

  return `${sample.latitude.toFixed(6)}, ${sample.longitude.toFixed(6)}`
}

function formatMockSpeedText(sample: LocationSample | null): string {
  if (!sample || !Number.isFinite(sample.speed)) {
    return '--'
  }

  return `${(Number(sample.speed) * 3.6).toFixed(1)} km/h`
}

function normalizeMockBridgeUrl(rawUrl: string): string {
  const trimmed = rawUrl.trim()
  if (!trimmed) {
    return DEFAULT_MOCK_LOCATION_BRIDGE_URL
  }

  let normalized = trimmed
  if (!/^wss?:\/\//i.test(normalized)) {
    normalized = `ws://${normalized.replace(/^\/+/, '')}`
  }

  if (!/\/mock-gps(?:\?.*)?$/i.test(normalized)) {
    normalized = normalized.replace(/\/+$/, '')
    normalized = `${normalized}/mock-gps`
  }

  return normalized
}

export class LocationController {
  callbacks: LocationControllerCallbacks
  realSource: RealLocationSource
  mockSource: MockLocationSource
  mockBridge: MockLocationBridge
  sourceMode: LocationSourceMode
  mockBridgeStatusText: string
  mockBridgeUrl: string
  mockChannelId: string

  constructor(callbacks: LocationControllerCallbacks) {
    this.callbacks = callbacks
    this.sourceMode = 'real'
    this.mockBridgeUrl = DEFAULT_MOCK_LOCATION_BRIDGE_URL
    this.mockBridgeStatusText = `未连接 (${this.mockBridgeUrl})`
    this.mockChannelId = 'default'

    const sourceCallbacks: LocationSourceCallbacks = {
      onLocation: (sample) => {
        this.callbacks.onLocation(sample)
        this.emitDebugState()
      },
      onStatus: (message) => {
        this.callbacks.onStatus(message)
        this.emitDebugState()
      },
      onError: (message) => {
        this.callbacks.onError(message)
        this.emitDebugState()
      },
    }

    this.realSource = new RealLocationSource(sourceCallbacks)
    this.mockSource = new MockLocationSource(sourceCallbacks)
    this.mockBridge = new MockLocationBridge({
      onOpen: () => {
        this.mockBridgeStatusText = `已连接 (${this.mockBridge.url})`
        this.callbacks.onStatus('模拟定位源已连接')
        this.emitDebugState()
      },
      onClose: () => {
        this.mockBridgeStatusText = `未连接 (${this.mockBridge.url})`
        this.callbacks.onStatus('模拟定位源已断开')
        this.emitDebugState()
      },
      onError: (message) => {
        this.mockBridgeStatusText = `连接失败 (${this.mockBridge.url})`
        this.callbacks.onError(`模拟定位源错误: ${message}`)
        this.emitDebugState()
      },
      onSample: (sample) => {
        this.mockSource.pushSample(sample)
        this.emitDebugState()
      },
    })
  }

  get listening(): boolean {
    return this.sourceMode === 'mock' ? this.mockSource.active : this.realSource.active
  }

  getDebugState(): LocationControllerDebugState {
    return {
      sourceMode: this.sourceMode,
      sourceModeText: formatSourceModeText(this.sourceMode),
      listening: this.listening,
      mockBridgeConnected: this.mockBridge.connected,
      mockBridgeStatusText: this.mockBridgeStatusText,
      mockBridgeUrlText: this.mockBridgeUrl,
      mockChannelIdText: this.mockChannelId,
      mockCoordText: formatMockCoordText(this.mockSource.lastSample),
      mockSpeedText: formatMockSpeedText(this.mockSource.lastSample),
    }
  }

  start(): void {
    this.getActiveSource().start()
    this.emitDebugState()
  }

  stop(): void {
    this.getActiveSource().stop()
    this.emitDebugState()
  }

  destroy(): void {
    this.realSource.destroy()
    this.mockSource.destroy()
    this.mockBridge.destroy()
    this.emitDebugState()
  }

  setSourceMode(mode: LocationSourceMode): void {
    if (this.sourceMode === mode) {
      this.callbacks.onStatus(`${formatSourceModeText(mode)}已启用`)
      this.emitDebugState()
      return
    }

    const wasListening = this.listening
    if (wasListening) {
      this.getActiveSource().stop()
    }
    this.sourceMode = mode

    if (wasListening) {
      this.getActiveSource().start()
    } else {
      this.callbacks.onStatus(`已切换到${formatSourceModeText(mode)}`)
    }

    this.emitDebugState()
  }

  setMockBridgeUrl(url: string): void {
    this.mockBridgeUrl = normalizeMockBridgeUrl(url)

    if (this.mockBridge.connected || this.mockBridge.connecting) {
      this.mockBridgeStatusText = `已设置新地址，重连生效 (${this.mockBridgeUrl})`
      this.callbacks.onStatus('模拟定位源地址已更新，重连后生效')
    } else {
      this.mockBridgeStatusText = `未连接 (${this.mockBridgeUrl})`
      this.callbacks.onStatus('模拟定位源地址已更新')
    }

    this.emitDebugState()
  }

  setMockChannelId(channelId: string): void {
    const normalized = String(channelId || '').trim() || 'default'
    this.mockChannelId = normalized
    this.mockBridge.setChannelId(normalized)
    this.callbacks.onStatus(`模拟定位通道已切换到 ${normalized}`)
    this.emitDebugState()
  }

  connectMockBridge(url = DEFAULT_MOCK_LOCATION_BRIDGE_URL): void {
    if (this.mockBridge.connected || this.mockBridge.connecting) {
      this.callbacks.onStatus('模拟定位源已连接')
      this.emitDebugState()
      return
    }

    const targetUrl = normalizeMockBridgeUrl(url === DEFAULT_MOCK_LOCATION_BRIDGE_URL ? this.mockBridgeUrl : url)
    this.mockBridgeUrl = targetUrl
    this.mockBridgeStatusText = `连接中 (${targetUrl})`
    this.emitDebugState()
    this.callbacks.onStatus('模拟定位源连接中')
    this.mockBridge.connect(targetUrl)
  }

  disconnectMockBridge(): void {
    if (!this.mockBridge.connected && !this.mockBridge.connecting) {
      this.callbacks.onStatus('模拟定位源未连接')
      this.emitDebugState()
      return
    }

    this.mockBridge.disconnect()
    this.mockBridgeStatusText = `未连接 (${this.mockBridge.url})`
    this.callbacks.onStatus('模拟定位源已断开')
    this.emitDebugState()
  }

  getActiveSource(): RealLocationSource | MockLocationSource {
    return this.sourceMode === 'mock' ? this.mockSource : this.realSource
  }

  emitDebugState(): void {
    if (this.callbacks.onDebugStateChange) {
      this.callbacks.onDebugStateChange(this.getDebugState())
    }
  }
}
