import { useAppUserStore } from '@/common/app/app-user-store.ts'
import Evt from '@/common/utils/evt.ts'
import Strings from '@/common/utils/strings.ts'
import type { R } from '@/common/utils/http-util.ts'
import { wsServerBaseUrl } from '@/common'
import type { App } from 'vue'

// readonly CONNECTING: 0;
// readonly OPEN: 1;
// readonly CLOSING: 2;
// readonly CLOSED: 3;


interface WsStatus {
  status: 'online' | 'offline' | 'connecting' | 'reconnecting',
  enable: boolean,
  lastTime: number,
}

const wsStatus: WsStatus = reactive({
  status: 'offline',
  enable: false,
  lastTime: 0,
})


const wsUrl = wsServerBaseUrl + (wsServerBaseUrl.endsWith('/') ? 'fdx' : '/fdx')
let websocket: WebSocket | null = null
let heartbeat: number | null = null

interface WebsocketMessage {
  action: 'publish' | 'subscribe' | 'unsubscribe' | 'ping' | 'pong';
  event: string;
  data?: any;
}

export interface Ws {
  connect: (url: string, param: Record<string, any>) => void;
  reconnect: (url: string, param: Record<string, any>) => void;
  start: () => void;
  stop: () => void;
  status: WsStatus;

  publish(event: string, data?: any): void;

  subscribe<T>(event: string, handler: (data?: R<T>) => void): void;

  unsubscribe(event: string, handler?: (data?: any) => void): void;
}

/**
 * 订阅事件
 * @param event 事件名称
 * @param handler 处理函数
 */
function subscribe<T>(event: string, handler: (data?: R<T>) => void) {
  Evt.on(event, (data) => {
    handler(data as R<T>)
  })
  console.log('订阅：', event)
  websocket!.send(
    JSON.stringify({
      action: 'subscribe',
      event,
    }),
  )
}

/**
 * 取消订阅事件
 * @param event 事件名称
 * @param handler 处理函数（可空）
 */
function unsubscribe(event: string, handler?: (data?: any) => void) {
  Evt.off(event, handler)
  console.log('取消订阅')
  websocket!.send(
    JSON.stringify({
      action: 'unsubscribe',
      event,
    }),
  )
}

/**
 * 发布数据
 * @param event 事件名称
 * @param data 数据
 */
function publish(event: string, data?: any) {
  websocket!.send(
    JSON.stringify({
      action: 'publish',
      event,
      data,
    }),
  )
}

function startHeartbeat() {
  console.log('启动心跳')
  heartbeat = setInterval(() => {
    if (new Date().getTime() - wsStatus.lastTime >= 9000) {
      console.log('心跳超时')
      wsStatus.status = 'offline'
      close()
      return
    }
    if (websocket == null || wsStatus.status !== 'online') {
      console.log('不发送心跳')
      return
    }
    websocket.send(
      JSON.stringify({
        action: 'ping',
        event: 'ping',
      }),
    )
  }, 5000)
}

function stopHeartbeat() {
  if (heartbeat != null) {
    clearInterval(heartbeat)
  }
  heartbeat = null
}

function onmessage(event: MessageEvent) {
  try {
    wsStatus.lastTime = new Date().getTime()
    const data = JSON.parse(event.data ?? '{}') as WebsocketMessage
    if (data.action === 'publish') {
      if (!Strings.isBlank(data.event)) {
        Evt.emit(data.event, data.data)
      }
    }
  } catch (e) {
    console.log('错误的消息')
  }
}

function onopen() {
  if (wsStatus.status !== 'connecting') {
    console.log(`状态错误：${wsStatus.status}`)
    return
  }
  wsStatus.status = 'online'
  wsStatus.lastTime = new Date().getTime()
  console.log('连接成功', wsStatus)
}

function onclose(event: CloseEvent) {
  console.log(`连接已关闭 (代码: ${event.code}, 原因: ${event.reason ?? '无'})`)
  if (!wsStatus.enable) {
    console.log('已禁用，无需重连')
    return
  }
  if (wsStatus.status !== 'connecting' && wsStatus.status !== 'online') {
    console.log(`状态错误：${wsStatus.status}`)
    return
  }
  wsStatus.status = 'offline'
  reconnect()
}

function onerror(event: Event) {
  console.log(`发生错误: ${event.type ?? '未知错误'}`)
}

/**
 * 关闭连接
 */
function close() {
  if (websocket != null) {
    console.log('关闭 Websocket')
    websocket.close()
    // websocket = null
  }
}

function start() {
  if (wsStatus.enable) {
    throw new Error(`状态错误：当前不是禁用状态`)
  }
  console.log('启动 Websocket')
  wsStatus.status = 'offline'
  wsStatus.enable = true
  wsStatus.lastTime = 0
  close()
  connect()
  startHeartbeat()
}

function stop() {
  wsStatus.status = 'offline'
  wsStatus.enable = false
  wsStatus.lastTime = 0
  stopHeartbeat()
  close()
}

/**
 * 新建连接
 * @param url 地址
 * @param param 参数
 */
function connect() {
  let url: string = wsUrl
  let param: Record<string, any> = {}
  if (!wsStatus.enable) {
    console.log('已禁用，不连接')
    return
  }
  if (wsStatus.status !== 'reconnecting' && wsStatus.status !== 'offline') {
    throw new Error(`状态错误：${wsStatus.status}`)
  }
  if (websocket != null) {
    throw new Error('请先关闭连接')
  }
  console.log('连接 Websocket')
  wsStatus.status = 'connecting'
  const appUserStore = useAppUserStore()
  param.authorization = appUserStore.token
  const params = []
  for (let k in param) {
    params.push(`${k}=${encodeURIComponent(param[k])}`)
  }
  url = url + '?' + params.join('&')
  websocket = new WebSocket(url)
  websocket.onopen = onopen
  websocket.onmessage = onmessage
  websocket.onclose = onclose
  websocket.onerror = onerror
}

function reconnect() {
  if (!wsStatus.enable) {
    console.log('已禁用，不重连接')
    return
  }
  if (wsStatus.status !== 'offline') {
    throw new Error(`状态错误：${wsStatus.status}`)
  }
  wsStatus.status = 'reconnecting'
  console.log('等待重连 Websocket')

  setTimeout(() => {
    if (!wsStatus.enable) {
      console.log('已禁用，不重连接')
      return
    }
    if (wsStatus.status !== 'reconnecting') return
    close()
    connect()
  }, 5000)
}

export const WsKey = Symbol() as InjectionKey<Ws>

function install(app: App) {
  app.provide(WsKey, {
    connect,
    reconnect,
    start,
    stop,
    status: wsStatus,
    publish,
    subscribe,
    unsubscribe,
  })

  Evt.on('connect_ws', start)
  Evt.on('disconnect_ws', stop)
}

export default {
  start,
  stop,
  connect,
  reconnect,
  close,
  publish,
  subscribe,
  unsubscribe,
  status: wsStatus,
  install,
}
