import { HeartRateController, type HeartRateControllerCallbacks, type HeartRateDiscoveredDevice } from './heartRateController'
import { DEFAULT_MOCK_HEART_RATE_BRIDGE_URL, MockHeartRateBridge } from './mockHeartRateBridge'

export type HeartRateSourceMode = 'real' | 'mock'

export interface HeartRateInputControllerCallbacks {
  onHeartRate: (bpm: number) => void
  onStatus: (message: string) => void
  onError: (message: string) => void
  onConnectionChange: (connected: boolean, deviceName: string | null) => void
  onDeviceListChange: (devices: HeartRateDiscoveredDevice[]) => void
  onDebugStateChange?: () => void
}

export interface HeartRateInputControllerDebugState {
  sourceMode: HeartRateSourceMode
  sourceModeText: string
  mockBridgeConnected: boolean
  mockBridgeStatusText: string
  mockBridgeUrlText: string
  mockChannelIdText: string
  mockHeartRateText: string
}

function formatSourceModeText(mode: HeartRateSourceMode): string {
  return mode === 'mock' ? '模拟心率' : '真实心率'
}

function formatMockHeartRateText(bpm: number | null): string {
  return bpm === null ? '--' : `${bpm} bpm`
}

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

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

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

  return normalized
}

export class HeartRateInputController {
  callbacks: HeartRateInputControllerCallbacks
  realController: HeartRateController
  mockBridge: MockHeartRateBridge
  sourceMode: HeartRateSourceMode
  mockBridgeStatusText: string
  mockBridgeUrl: string
  mockChannelId: string
  mockBpm: number | null

  constructor(callbacks: HeartRateInputControllerCallbacks) {
    this.callbacks = callbacks
    this.sourceMode = 'real'
    this.mockBridgeUrl = DEFAULT_MOCK_HEART_RATE_BRIDGE_URL
    this.mockBridgeStatusText = `未连接 (${this.mockBridgeUrl})`
    this.mockChannelId = 'default'
    this.mockBpm = null

    const realCallbacks: HeartRateControllerCallbacks = {
      onHeartRate: (bpm) => {
        if (this.sourceMode !== 'real') {
          return
        }

        this.callbacks.onHeartRate(bpm)
        this.emitDebugState()
      },
      onStatus: (message) => {
        if (this.sourceMode !== 'real') {
          return
        }

        this.callbacks.onStatus(message)
        this.emitDebugState()
      },
      onError: (message) => {
        if (this.sourceMode !== 'real') {
          return
        }

        this.callbacks.onError(message)
        this.emitDebugState()
      },
      onConnectionChange: (connected, deviceName) => {
        if (this.sourceMode !== 'real') {
          return
        }

        this.callbacks.onConnectionChange(connected, deviceName)
        this.emitDebugState()
      },
      onDeviceListChange: (devices) => {
        this.callbacks.onDeviceListChange(devices)
        this.emitDebugState()
      },
    }

    this.realController = new HeartRateController(realCallbacks)
    this.mockBridge = new MockHeartRateBridge({
      onOpen: () => {
        this.mockBridgeStatusText = `已连接 (${this.mockBridge.url})`
        if (this.sourceMode === 'mock') {
          this.callbacks.onConnectionChange(true, '模拟心率源')
          this.callbacks.onStatus('模拟心率源已连接，等待外部输入')
        }
        this.emitDebugState()
      },
      onClose: () => {
        this.mockBridgeStatusText = `未连接 (${this.mockBridge.url})`
        if (this.sourceMode === 'mock') {
          this.callbacks.onConnectionChange(false, null)
          this.callbacks.onStatus('模拟心率源已断开')
        }
        this.emitDebugState()
      },
      onError: (message) => {
        this.mockBridgeStatusText = `连接失败 (${this.mockBridge.url})`
        if (this.sourceMode === 'mock') {
          this.callbacks.onConnectionChange(false, null)
          this.callbacks.onError(`模拟心率源错误: ${message}`)
        }
        this.emitDebugState()
      },
      onBpm: (bpm) => {
        this.mockBpm = bpm
        if (this.sourceMode === 'mock') {
          this.callbacks.onHeartRate(bpm)
        }
        this.emitDebugState()
      },
    })
  }

  get currentDeviceId(): string | null {
    if (this.sourceMode === 'mock') {
      return this.mockBridge.connected ? 'mock-heart-rate' : null
    }

    return this.realController.currentDeviceId
  }

  get currentDeviceName(): string | null {
    if (this.sourceMode === 'mock') {
      return this.mockBridge.connected ? '模拟心率源' : null
    }

    return this.realController.currentDeviceName
  }

  get connected(): boolean {
    return this.sourceMode === 'mock' ? this.mockBridge.connected : this.realController.connected
  }

  get connecting(): boolean {
    return this.sourceMode === 'mock' ? this.mockBridge.connecting : this.realController.connecting
  }

  get scanning(): boolean {
    return this.sourceMode === 'mock' ? false : this.realController.scanning
  }

  get reconnecting(): boolean {
    return this.sourceMode === 'mock' ? false : this.realController.reconnecting
  }

  get disconnecting(): boolean {
    return this.sourceMode === 'mock' ? false : this.realController.disconnecting
  }

  get discoveredDevices(): HeartRateDiscoveredDevice[] {
    return this.realController.discoveredDevices
  }

  get lastDeviceId(): string | null {
    return this.realController.lastDeviceId
  }

  get lastDeviceName(): string | null {
    return this.realController.lastDeviceName
  }

  getDebugState(): HeartRateInputControllerDebugState {
    return {
      sourceMode: this.sourceMode,
      sourceModeText: formatSourceModeText(this.sourceMode),
      mockBridgeConnected: this.mockBridge.connected,
      mockBridgeStatusText: this.mockBridgeStatusText,
      mockBridgeUrlText: this.mockBridgeUrl,
      mockChannelIdText: this.mockChannelId,
      mockHeartRateText: formatMockHeartRateText(this.mockBpm),
    }
  }

  startScanAndConnect(): void {
    if (this.sourceMode === 'mock') {
      this.callbacks.onStatus(this.mockBridge.connected ? '模拟心率源已连接' : '当前为模拟心率模式，请连接模拟源')
      this.emitDebugState()
      return
    }

    this.realController.startScanAndConnect()
  }

  disconnect(): void {
    if (this.sourceMode === 'mock') {
      if (!this.mockBridge.connected && !this.mockBridge.connecting) {
        this.callbacks.onStatus('模拟心率源未连接')
        this.emitDebugState()
        return
      }

      this.mockBridge.disconnect()
      this.emitDebugState()
      return
    }

    this.realController.disconnect()
  }

  destroy(): void {
    this.realController.destroy()
    this.mockBridge.destroy()
  }

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

    const previousMode = this.sourceMode
    this.sourceMode = mode

    if (previousMode === 'real') {
      this.realController.disconnect()
    } else {
      this.callbacks.onConnectionChange(false, null)
    }

    const activeDeviceName = this.currentDeviceName
    this.callbacks.onConnectionChange(this.connected, activeDeviceName)
    this.callbacks.onStatus(mode === 'mock' ? '已切换到模拟心率模式' : '已切换到真实心率模式')
    this.emitDebugState()
  }

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

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

    this.emitDebugState()
  }

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

  connectMockBridge(url = DEFAULT_MOCK_HEART_RATE_BRIDGE_URL): void {
    if (this.mockBridge.connected || this.mockBridge.connecting) {
      if (this.sourceMode === 'mock') {
        this.callbacks.onStatus('模拟心率源已连接')
      }
      this.emitDebugState()
      return
    }

    const targetUrl = normalizeMockBridgeUrl(url === DEFAULT_MOCK_HEART_RATE_BRIDGE_URL ? this.mockBridgeUrl : url)
    this.mockBridgeUrl = targetUrl
    this.mockBridgeStatusText = `连接中 (${targetUrl})`
    if (this.sourceMode === 'mock') {
      this.callbacks.onStatus('模拟心率源连接中')
    }
    this.emitDebugState()
    this.mockBridge.connect(targetUrl)
  }

  disconnectMockBridge(): void {
    if (!this.mockBridge.connected && !this.mockBridge.connecting) {
      if (this.sourceMode === 'mock') {
        this.callbacks.onStatus('模拟心率源未连接')
      }
      this.emitDebugState()
      return
    }

    this.mockBridge.disconnect()
    this.mockBridgeStatusText = `未连接 (${this.mockBridge.url})`
    this.emitDebugState()
  }

  connectToDiscoveredDevice(deviceId: string): void {
    if (this.sourceMode !== 'real') {
      this.callbacks.onStatus('当前为模拟心率模式，无法连接真实心率带')
      this.emitDebugState()
      return
    }

    this.realController.connectToDiscoveredDevice(deviceId)
  }

  clearPreferredDevice(): void {
    this.realController.clearPreferredDevice()
    this.emitDebugState()
  }

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