const DEFAULT_DEBUG_LOG_URL = 'wss://gs.gotomars.xyz/debug-log'
const MAX_QUEUED_LOGS = 80

export type MockSimulatorDebugLogLevel = 'info' | 'warn' | 'error'

export interface MockSimulatorDebugLoggerState {
  enabled: boolean
  connected: boolean
  connecting: boolean
  url: string
  statusText: string
}

export interface MockSimulatorDebugLogEntry {
  type: 'debug-log'
  timestamp: number
  channelId?: string
  scope: string
  level: MockSimulatorDebugLogLevel
  message: string
  payload?: Record<string, unknown>
}

function normalizeMockSimulatorChannelId(rawChannelId: string | null | undefined): string {
  const trimmed = String(rawChannelId || '').trim()
  return trimmed || 'default'
}

function normalizeMockSimulatorLogUrl(rawUrl: string): string {
  const trimmed = String(rawUrl || '').trim()
  if (!trimmed) {
    return DEFAULT_DEBUG_LOG_URL
  }

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

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

  return normalized
}

export class MockSimulatorDebugLogger {
  socketTask: WechatMiniprogram.SocketTask | null
  enabled: boolean
  connected: boolean
  connecting: boolean
  url: string
  channelId: string
  queue: MockSimulatorDebugLogEntry[]
  onStateChange?: (state: MockSimulatorDebugLoggerState) => void

  constructor(onStateChange?: (state: MockSimulatorDebugLoggerState) => void) {
    this.socketTask = null
    this.enabled = false
    this.connected = false
    this.connecting = false
    this.url = DEFAULT_DEBUG_LOG_URL
    this.channelId = 'default'
    this.queue = []
    this.onStateChange = onStateChange
  }

  getState(): MockSimulatorDebugLoggerState {
    return {
      enabled: this.enabled,
      connected: this.connected,
      connecting: this.connecting,
      url: this.url,
      statusText: !this.enabled
        ? `已关闭 (${this.url})`
        : this.connected
          ? `已连接 (${this.url})`
          : this.connecting
            ? `连接中 (${this.url})`
            : `未连接 (${this.url})`,
    }
  }

  emitState(): void {
    if (this.onStateChange) {
      this.onStateChange(this.getState())
    }
  }

  setEnabled(enabled: boolean): void {
    if (this.enabled === enabled) {
      return
    }

    this.enabled = enabled
    if (!enabled) {
      this.disconnect()
      this.queue = []
      this.emitState()
      return
    }

    this.emitState()
    this.connect()
  }

  setUrl(url: string): void {
    const nextUrl = normalizeMockSimulatorLogUrl(url)
    if (this.url === nextUrl) {
      return
    }

    this.url = nextUrl
    if (!this.enabled) {
      this.emitState()
      return
    }

    this.disconnect()
    this.emitState()
    this.connect()
  }

  setChannelId(channelId: string): void {
    this.channelId = normalizeMockSimulatorChannelId(channelId)
  }

  log(
    scope: string,
    level: MockSimulatorDebugLogLevel,
    message: string,
    payload?: Record<string, unknown>,
  ): void {
    if (!this.enabled) {
      return
    }

    const entry: MockSimulatorDebugLogEntry = {
      type: 'debug-log',
      timestamp: Date.now(),
      channelId: this.channelId,
      scope,
      level,
      message,
      ...(payload ? { payload } : {}),
    }

    if (this.connected && this.socketTask) {
      this.send(entry)
      return
    }

    this.queue.push(entry)
    if (this.queue.length > MAX_QUEUED_LOGS) {
      this.queue.splice(0, this.queue.length - MAX_QUEUED_LOGS)
    }
    this.connect()
  }

  disconnect(): void {
    const socketTask = this.socketTask
    this.socketTask = null
    this.connected = false
    this.connecting = false
    if (socketTask) {
      try {
        socketTask.close({})
      } catch (_error) {
        // noop
      }
    }
    this.emitState()
  }

  destroy(): void {
    this.disconnect()
    this.queue = []
  }

  connect(): void {
    if (!this.enabled || this.connected || this.connecting) {
      return
    }

    this.connecting = true
    this.emitState()
    try {
      const socketTask = wx.connectSocket({
        url: this.url,
      })
      this.socketTask = socketTask

      socketTask.onOpen(() => {
        this.connected = true
        this.connecting = false
        this.emitState()
        this.send({
          type: 'debug-log',
          timestamp: Date.now(),
          channelId: this.channelId,
          scope: 'logger',
          level: 'info',
          message: 'logger channel connected',
          payload: {
            url: this.url,
            channelId: this.channelId,
          },
        })
        this.flush()
      })

      socketTask.onClose(() => {
        this.connected = false
        this.connecting = false
        this.socketTask = null
        this.emitState()
      })

      socketTask.onError(() => {
        this.connected = false
        this.connecting = false
        this.socketTask = null
        this.emitState()
      })

      socketTask.onMessage(() => {
        // 模拟器会广播所有消息，debug logger 不消费回包。
      })
    } catch (_error) {
      this.connected = false
      this.connecting = false
      this.socketTask = null
      this.emitState()
    }
  }

  flush(): void {
    if (!this.connected || !this.socketTask || !this.queue.length) {
      return
    }

    const pending = this.queue.splice(0, this.queue.length)
    pending.forEach((entry) => {
      this.send(entry)
    })
  }

  send(entry: MockSimulatorDebugLogEntry): void {
    if (!this.socketTask || !this.connected) {
      return
    }

    try {
      this.socketTask.send({
        data: JSON.stringify(entry),
      })
    } catch (_error) {
      this.connected = false
      this.connecting = false
      this.socketTask = null
      this.emitState()
    }
  }
}
