import type { BrowserWindow } from 'electron'
import { NTClass, NTMethod } from './ntcall'
import { log } from '@/common/utils'
import { randomUUID } from 'node:crypto'
import { Dict } from 'cosmokit'

export const hookApiCallbacks: Record<string, (res: any) => void> = {}

export enum ReceiveCmdS {
  RECENT_CONTACT = 'nodeIKernelRecentContactListener/onRecentContactListChangedVer2',
  UPDATE_MSG = 'nodeIKernelMsgListener/onMsgInfoListUpdate',
  UPDATE_ACTIVE_MSG = 'nodeIKernelMsgListener/onActiveMsgInfoUpdate',
  NEW_MSG = 'nodeIKernelMsgListener/onRecvMsg',
  NEW_ACTIVE_MSG = 'nodeIKernelMsgListener/onRecvActiveMsg',
  SELF_SEND_MSG = 'nodeIKernelMsgListener/onAddSendMsg',
  USER_INFO = 'nodeIKernelProfileListener/onProfileSimpleChanged',
  USER_DETAIL_INFO = 'nodeIKernelProfileListener/onProfileDetailInfoChanged',
  GROUPS = 'nodeIKernelGroupListener/onGroupListUpdate',
  GROUPS_STORE = 'onGroupListUpdate',
  GROUP_MEMBER_INFO_UPDATE = 'nodeIKernelGroupListener/onMemberInfoChange',
  FRIENDS = 'onBuddyListChange',
  MEDIA_DOWNLOAD_COMPLETE = 'nodeIKernelMsgListener/onRichMediaDownloadComplete',
  UNREAD_GROUP_NOTIFY = 'nodeIKernelGroupListener/onGroupNotifiesUnreadCountUpdated',
  GROUP_NOTIFY = 'nodeIKernelGroupListener/onGroupSingleScreenNotifies',
  FRIEND_REQUEST = 'nodeIKernelBuddyListener/onBuddyReqChange',
  SELF_STATUS = 'nodeIKernelProfileListener/onSelfStatusChanged',
  CACHE_SCAN_FINISH = 'nodeIKernelStorageCleanListener/onFinishScan',
  MEDIA_UPLOAD_COMPLETE = 'nodeIKernelMsgListener/onRichMediaUploadComplete',
  SKEY_UPDATE = 'onSkeyUpdate',
}

type NTReturnData = [
  {
    type: 'request'
    eventName: NTClass
    callbackId?: string
  },
  {
    cmdName: ReceiveCmdS
    cmdType: 'event'
    payload: unknown
  }[]
]

const logHook = false

const receiveHooks: Array<{
  method: ReceiveCmdS[]
  hookFunc: (payload: any) => void | Promise<void>
  id: string
}> = []

const callHooks: Array<{
  method: NTMethod[]
  hookFunc: (callParams: unknown[]) => void | Promise<void>
}> = []

export function hookNTQQApiReceive(window: BrowserWindow, onlyLog: boolean) {
  window.webContents.send = new Proxy(window.webContents.send, {
    apply(target, thisArg, args: [channel: string, ...args: NTReturnData]) {
      try {
        if (logHook && !args[1]?.eventName?.startsWith('ns-LoggerApi')) {
          log('received ntqq api message', args)
        }
      } catch { }
      if (!onlyLog) {
        if (args[2] instanceof Array) {
          for (const receiveData of args[2]) {
            const ntMethodName = receiveData.cmdName
            for (const hook of receiveHooks) {
              if (hook.method.includes(ntMethodName)) {
                Promise.resolve(hook.hookFunc(receiveData.payload))
              }
            }
          }
        }
        if (args[1]?.callbackId) {
          const callbackId = args[1].callbackId
          if (hookApiCallbacks[callbackId]) {
            Promise.resolve(hookApiCallbacks[callbackId](args[2]))
            delete hookApiCallbacks[callbackId]
          }
        }
      }
      return target.apply(thisArg, args)
    },
  })
}

export function hookNTQQApiCall(window: BrowserWindow, onlyLog: boolean) {
  const webContents = window.webContents as Dict
  const ipc_message_proxy = webContents._events['-ipc-message']?.[0] || webContents._events['-ipc-message']

  const proxyIpcMsg = new Proxy(ipc_message_proxy, {
    apply(target, thisArg, args) {
      const isLogger = args[3]?.[0]?.eventName?.startsWith('ns-LoggerApi')
      if (!isLogger) {
        try {
          logHook && log('call NTQQ api', thisArg, args)
        } catch (e) { }
        if (!onlyLog) {
          try {
            const _args: unknown[] = args[3][1]
            const cmdName = _args[0] as NTMethod
            const callParams = _args.slice(1)
            callHooks.forEach((hook) => {
              if (hook.method.includes(cmdName)) {
                Promise.resolve(hook.hookFunc(callParams))
              }
            })
          } catch { }
        }
      }
      return target.apply(thisArg, args)
    },
  })
  if (webContents._events['-ipc-message']?.[0]) {
    webContents._events['-ipc-message'][0] = proxyIpcMsg
  } else {
    webContents._events['-ipc-message'] = proxyIpcMsg
  }

  /*const ipc_invoke_proxy = webContents._events['-ipc-invoke']?.[0] || webContents._events['-ipc-invoke']
  const proxyIpcInvoke = new Proxy(ipc_invoke_proxy, {
    apply(target, thisArg, args) {
      //HOOK_LOG && log('call NTQQ invoke api', thisArg, args)
      args[0]['_replyChannel']['sendReply'] = new Proxy(args[0]['_replyChannel']['sendReply'], {
        apply(sendtarget, sendthisArg, sendargs) {
          sendtarget.apply(sendthisArg, sendargs)
        },
      })
      const ret = target.apply(thisArg, args)
      //HOOK_LOG && log('call NTQQ invoke api return', ret)
      return ret
    },
  })
  if (webContents._events['-ipc-invoke']?.[0]) {
    webContents._events['-ipc-invoke'][0] = proxyIpcInvoke
  } else {
    webContents._events['-ipc-invoke'] = proxyIpcInvoke
  }*/
}

export function registerReceiveHook<PayloadType>(
  method: string | string[],
  hookFunc: (payload: PayloadType) => void,
): string {
  const id = randomUUID()
  if (!Array.isArray(method)) {
    method = [method]
  }
  receiveHooks.push({
    method: method as ReceiveCmdS[],
    hookFunc,
    id,
  })
  return id
}

export function registerCallHook(
  method: NTMethod | NTMethod[],
  hookFunc: (callParams: unknown[]) => void | Promise<void>,
): void {
  if (!Array.isArray(method)) {
    method = [method]
  }
  callHooks.push({
    method,
    hookFunc,
  })
}

export function removeReceiveHook(id: string) {
  const index = receiveHooks.findIndex((h) => h.id === id)
  receiveHooks.splice(index, 1)
}