/**
 * Remote browser session — canvas screencast over /ws/browser/{session_id}.
 */
import { onUnmounted, ref, shallowRef } from 'vue'
import { http } from '@/api'
import { useAuthStore } from '@/stores/auth'
import { buildWebSocketUrl } from '@/utils/wsUrl'

export interface BrowserSessionInfo {
  session_id: string
  viewport_w: number
  viewport_h: number
}

export function useRemoteBrowserSession() {
  const connected = ref(false)
  const connecting = ref(false)
  const remoteError = ref<string | null>(null)
  const sessionId = shallowRef<string | null>(null)
  const viewportW = ref(1280)
  const viewportH = ref(720)

  let ws: WebSocket | null = null
  let canvasEl: HTMLCanvasElement | null = null
  let onPageInfo: ((url: string, title: string) => void) | null = null
  let dragActive = false
  let lastPointerX = 0
  let lastPointerY = 0

  function setCanvas(el: HTMLCanvasElement | null) {
    canvasEl = el
  }

  function setPageInfoHandler(fn: (url: string, title: string) => void) {
    onPageInfo = fn
  }

  function send(msg: Record<string, unknown>) {
    if (!ws || ws.readyState !== WebSocket.OPEN) return
    ws.send(JSON.stringify(msg))
  }

  function drawFrame(dataB64: string, metadata?: { deviceWidth?: number; deviceHeight?: number }) {
    if (!canvasEl || !dataB64) return
    const img = new Image()
    img.onload = () => {
      if (!canvasEl) return
      const w = metadata?.deviceWidth || img.width
      const h = metadata?.deviceHeight || img.height
      if (canvasEl.width !== w) canvasEl.width = w
      if (canvasEl.height !== h) canvasEl.height = h
      const ctx = canvasEl.getContext('2d')
      ctx?.drawImage(img, 0, 0, w, h)
    }
    img.src = `data:image/jpeg;base64,${dataB64}`
  }

  function canvasCoords(event: MouseEvent): { x: number; y: number } {
    if (!canvasEl) return { x: 0, y: 0 }
    const rect = canvasEl.getBoundingClientRect()
    const scaleX = canvasEl.width / rect.width
    const scaleY = canvasEl.height / rect.height
    return {
      x: (event.clientX - rect.left) * scaleX,
      y: (event.clientY - rect.top) * scaleY,
    }
  }

  function sendPointerMove(x: number, y: number) {
    const dx = x - lastPointerX
    const dy = y - lastPointerY
    const dist = Math.sqrt(dx * dx + dy * dy)
    const steps = Math.max(1, Math.min(25, Math.ceil(dist / 6)))
    send({ type: 'MOUSE', event: 'move', x, y, steps })
    lastPointerX = x
    lastPointerY = y
  }

  function onWindowMouseMove(e: MouseEvent) {
    if (!dragActive || !canvasEl) return
    const { x, y } = canvasCoords(e)
    sendPointerMove(x, y)
  }

  function endPointerDrag(e: MouseEvent) {
    if (!dragActive) return
    dragActive = false
    window.removeEventListener('mousemove', onWindowMouseMove)
    window.removeEventListener('mouseup', endPointerDrag)
    const { x, y } = canvasCoords(e)
    const button = e.button === 2 ? 'right' : e.button === 1 ? 'middle' : 'left'
    send({ type: 'MOUSE', event: 'up', x, y, button })
  }

  function onCanvasMouseMove(e: MouseEvent) {
    if (dragActive) return
    const { x, y } = canvasCoords(e)
    sendPointerMove(x, y)
  }

  function onCanvasMouseDown(e: MouseEvent) {
    e.preventDefault()
    const { x, y } = canvasCoords(e)
    lastPointerX = x
    lastPointerY = y
    const button = e.button === 2 ? 'right' : e.button === 1 ? 'middle' : 'left'
    send({ type: 'MOUSE', event: 'down', x, y, button })
    dragActive = true
    window.addEventListener('mousemove', onWindowMouseMove)
    window.addEventListener('mouseup', endPointerDrag)
  }

  function onCanvasMouseUp(e: MouseEvent) {
    endPointerDrag(e)
  }

  function onCanvasWheel(e: WheelEvent) {
    e.preventDefault()
    const { x, y } = canvasCoords(e)
    send({ type: 'MOUSE', event: 'wheel', x, y, delta_y: e.deltaY })
  }

  function onCanvasKeyDown(e: KeyboardEvent) {
    e.preventDefault()
    send({ type: 'KEY', event: 'down', key: e.key, code: e.code })
    if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
      send({ type: 'KEY', event: 'type', text: e.key })
    }
  }

  function disconnect() {
    connected.value = false
    if (ws) {
      ws.close()
      ws = null
    }
    sessionId.value = null
  }

  async function connect(viewportWIn = 1280, viewportHIn = 720) {
    if (connecting.value || connected.value) return
    connecting.value = true
    remoteError.value = null
    try {
      const status = await http.get<{ enabled: boolean }>('/browser/status')
      if (!status.enabled) {
        remoteError.value = '服务端浏览器未启用'
        return
      }
      const created = await http.post<BrowserSessionInfo>('/browser/sessions', {
        viewport_w: viewportWIn,
        viewport_h: viewportHIn,
      })
      sessionId.value = created.session_id
      viewportW.value = created.viewport_w
      viewportH.value = created.viewport_h

      const token = useAuthStore().token
      if (!token) {
        remoteError.value = '未登录'
        await http.delete(`/browser/sessions/${created.session_id}`).catch(() => {})
        sessionId.value = null
        return
      }
      const url = buildWebSocketUrl(`/ws/browser/${created.session_id}`, { token })
      try {
        await new Promise<void>((resolve, reject) => {
          const socket = new WebSocket(url)
          ws = socket
          socket.onopen = () => {
            connected.value = true
            resolve()
          }
          socket.onerror = () => reject(new Error('WebSocket 连接失败'))
          socket.onclose = () => {
            connected.value = false
          }
          socket.onmessage = (ev) => {
            try {
              const msg = JSON.parse(String(ev.data)) as Record<string, unknown>
              const type = msg.type as string
              if (type === 'SCREENCAST_FRAME') {
                drawFrame(String(msg.data_b64 || ''), msg.metadata as { deviceWidth?: number; deviceHeight?: number })
              } else if (type === 'PAGE_INFO') {
                onPageInfo?.(String(msg.url || ''), String(msg.title || ''))
              } else if (type === 'ERROR') {
                remoteError.value = String(msg.message || '远程浏览器错误')
              } else if (type === 'BROWSER_INIT') {
                viewportW.value = Number(msg.viewport_w) || viewportW.value
                viewportH.value = Number(msg.viewport_h) || viewportH.value
              }
            } catch {
              /* ignore malformed */
            }
          }
        })
      } catch (wsErr) {
        await http.delete(`/browser/sessions/${created.session_id}`).catch(() => {})
        throw wsErr
      }
    } catch (e) {
      remoteError.value = e instanceof Error ? e.message : '无法连接远程浏览器'
      disconnect()
    } finally {
      connecting.value = false
    }
  }

  function navigate(url: string) {
    send({ type: 'NAVIGATE', url })
  }

  function reload() {
    send({ type: 'RELOAD' })
  }

  function goBack() {
    send({ type: 'GO_BACK' })
  }

  function goForward() {
    send({ type: 'GO_FORWARD' })
  }

  function resizeViewport(width: number, height: number) {
    send({ type: 'VIEWPORT', width, height })
  }

  onUnmounted(() => {
    window.removeEventListener('mousemove', onWindowMouseMove)
    window.removeEventListener('mouseup', endPointerDrag)
    disconnect()
  })

  return {
    connected,
    connecting,
    remoteError,
    sessionId,
    viewportW,
    viewportH,
    setCanvas,
    setPageInfoHandler,
    connect,
    disconnect,
    navigate,
    reload,
    goBack,
    goForward,
    resizeViewport,
    onCanvasMouseMove,
    onCanvasMouseDown,
    onCanvasMouseUp,
    onCanvasWheel,
    onCanvasKeyDown,
  }
}
