// 接口地址
const api = 'https://api.pandorastudio.cn/'
let messages = []
let timer = null
// 超时时间
const timeout = 60
// 默认加载文字
const defaultLoadingText = '正在思考中...'
// 思考时间过长加载文字
const longLoadingText = '问题有点难，正在思考中...'
// 加载文字
let loadingText = defaultLoadingText
// 历史记录文件夹入口
let historyDirEntry = null
// APP目录入口
let appDirEntry = null
const md = markdownit()
md.set({ linkify: true, typographer: true, breaks: true })
// 默认设置
let settings = {
  // 是否开启自动保存
  autoSave: true,
  // gpt模型
  model: 'gpt-3.5-turbo',
  // 是否使用流式对话
  useStream: true,
  // 创造性程度
  temperature: 1,
}
// 用户数据
let userData = {
  // 历史文件
  history: 'history.json',
  // 设置文件
  settings: 'settings.json',
}
// websocket
let socket = null
// 超时断开websocket 30s
const socketTimeOut = 30
// socket超时计时器
let socketTimer = null
// 服务端是否开始处理
let isStreamStart = false
// 用户重试次数
let retryCount = 0
// 重试次数上限
const retryLimit = 3

// 流式对话
function streamChat() {
  const decoder = new TextDecoder('utf-8')
  const div = document.createElement('div')
  // 给div添加class(left和pending)
  div.className = 'left pending'
  // 时间戳id
  div.id = Date.now()
  let streamOutput = ''

  // 插入div
  $('article').append(div)
  // 禁用提交按钮
  $('#submit').attr('disabled', true)

  if (!socket) {
    // 连接websocket
    socket = io(api)

    // 创建socket超时计时器
    let socketTime = 0
    socketTimer = setInterval(() => {
      // 如果超时
      if (socketTime > socketTimeOut && !isStreamStart) {
        clearInterval(socketTimer)
        // 断开连接
        socket.close()
        // 清空socket
        socket = null
        // 移除最新创建的div
        $('article').get.lastElementChild.remove()
        confirm({ content: `连接超时，是否重新发问？`, confirmText: '重新发问' }).then(questionSubmit)
      } else {
        socketTime++
      }
    }, 1000)

    // 监听连接
    socket.on('connect', () => {
      // 监听消息
      socket.on('chatResult', res => {
        if (res.code == 200) {
          // 获取最新创建的div
          const newDiv = $('article').get.lastElementChild

          try {
            const chunk = decoder.decode(res.chunk)
            const lines = chunk.split('\n')
            const parsedLines = lines
              .map(line => line.replace(/^data: /, '').trim()) // Remove the "data: " prefix
              .filter(line => line !== '' && line !== '[DONE]') // Remove empty lines and "[DONE]"
              .map(line => JSON.parse(line)) // Parse the JSON string

            // 移除pending
            newDiv.classList.remove('pending')
            isStreamStart = true

            for (const parsedLine of parsedLines) {
              const { choices } = parsedLine
              const { delta, finish_reason } = choices[0]
              const { content } = delta
              // Update the UI with the new content
              if (content) {
                streamOutput += content
                newDiv.innerText += content

                //滚动到底部
                $('article').get.scrollTo({ top: $('article').get.scrollHeight, behavior: 'smooth' })
              }

              // 对话结束
              if (finish_reason === 'stop') {
                clearInterval(socketTimer)
                newDiv.innerHTML = md.render(newDiv.innerText)
                // 高亮html
                newDiv.querySelectorAll('code').forEach(el => {
                  hljs.highlightElement(el)
                })
                isStreamStart = false
                navigator.vibrate(300)

                // 添加复制按钮
                addCopyBtn(newDiv)
                //滚动到底部
                $('article').get.scrollTo({ top: $('article').get.scrollHeight, behavior: 'smooth' })
                // 启用reload按钮
                $('#reload').removeAttr('disabled')
                // 清空输入框所有内容
                $('textarea').val('')
                messages.push({ role: 'assistant', content: streamOutput })
                // 是否开启自动保存
                settings.autoSave && autoSave()
              }
            }
          } catch (err) {
            isStreamStart = false
            clearInterval(socketTimer)
            confirm({ content: `网络发生错误`, confirmText: '重新发送' }).then(questionSubmit)
            // 移除最新创建的div
            newDiv.remove()
          }
        } else {
          isStreamStart = false
          clearInterval(socketTimer)
          confirm({ content: `连接发生错误`, confirmText: '重新发送' }).then(questionSubmit)
          // 移除最新创建的div
          $('article').get.lastElementChild.remove()
        }
      })

      // 监听错误
      socket.on('error', () => {
        isStreamStart = false
        clearInterval(socketTimer)
        confirm({ content: `连接发生错误`, confirmText: '重新发送' }).then(questionSubmit)
        // 移除最新创建的div
        $('article').get.lastElementChild.remove()
      })
    })
  }

  // 发送消息
  const { model, temperature } = settings
  socket.emit('chat', { messages, model, temperature })
}

// 标准对话
function standardChat() {
  let loadTime = 0
  // 恢复默认加载文字
  loadingText = defaultLoadingText
  showLoading(`${loadingText} ${loadTime++}s`)
  timer = setInterval(() => {
    // 如果超时
    if (loadTime > timeout) {
      clearInterval(timer)
      hideLoading()
      confirm({ content: `连接超时，是否重新发问？`, confirmText: '重新发问' }).then(questionSubmit)

      // 取消请求
      cancelAjax()
    } else if (loadTime > 10) {
      loadingText = longLoadingText
      showLoading(`${loadingText} ${loadTime++}s`)
    } else {
      showLoading(`${loadingText} ${loadTime++}s`)
    }
  }, 1000)

  // 发送消息
  const { model, temperature } = settings
  $()
    .ajax({
      url: `${api}common/chat`,
      type: 'POST',
      data: { messages: JSON.stringify(messages), model, temperature },
      async: true,
    })
    .then(res => {
      const { code, data } = res
      if (code == 200) {
        // 获取data最后一条消息
        const lastMessage = data[data.length - 1]
        const div = document.createElement('div')
        div.innerHTML = md.render(lastMessage.message.content)
        div.className = 'left'
        // 高亮html
        div.querySelectorAll('code').forEach(el => {
          hljs.highlightElement(el)
        })

        // 添加复制按钮
        addCopyBtn(div)
        $('article').append(div)
        messages.push({ role: 'assistant', content: lastMessage.message.content })
        // 清空输入框所有内容
        $('textarea').val('')
        navigator.vibrate(300)

        // 是否开启自动保存
        settings.autoSave && autoSave()
      } else {
        const { message } = JSON.parse(res.err).error
        alert(message)
      }
    })
    .catch(err => {
      confirm({ content: `发生错误：${JSON.stringify(err)}`, confirmText: '重新发送' }).then(questionSubmit)
    })
    .finally(() => {
      clearInterval(timer)
      hideLoading()

      //滚动到底部
      $('article').get.scrollTo({ top: $('article').get.scrollHeight, behavior: 'smooth' })

      // 启用reload按钮
      $('#reload').removeAttr('disabled')
      // 禁用提交按钮
      $('#submit').attr('disabled', true)
    })
}

// 清空所有记录
function clearChat() {
  return new Promise((resolve, reject) => {
    $('article').empty()
    messages = []
    $('#reload').attr('disabled', true)

    // 清空本地历史记录文件
    createFile(historyDirEntry, userData.history).then(fileEntry => {
      // 写入历史记录文件
      writeFile(fileEntry, '').then(resolve).catch(reject)
    })
  })
}

// 提交问题
function questionSubmit() {
  const question = $('textarea').val().trim()
  if (question == '') {
    alert('请输入内容')
    return
  }
  $('textarea').blur()
  $('article').append(`<p class="right">${question.toString()}</p>`)
  //延时滚动到底部
  $('article').get.scrollTo({ top: $('article').get.scrollHeight, behavior: 'smooth' })
  messages.push({ role: 'user', content: question.toString() })

  // 判断是否使用流式对话
  if (settings.useStream) {
    streamChat()
  } else {
    standardChat()
  }
}

// 创建本地目录
function createDirectory(rootDirEntry, folderName) {
  return new Promise((resolve, reject) => {
    rootDirEntry.getDirectory(
      folderName,
      { create: true },
      function (dirEntry) {
        resolve(dirEntry)
      },
      reject
    )
  })
}

// 创建本地提问历史记录
function createFile(dirEntry, fileName) {
  return new Promise((resolve, reject) => {
    dirEntry.getFile(
      fileName,
      { create: true, exclusive: false },
      function (fileEntry) {
        resolve(fileEntry)
      },
      reject
    )
  })
}

// 写入本地提问历史记录
function writeFile(fileEntry, dataObj) {
  return new Promise((resolve, reject) => {
    fileEntry.createWriter(function (fileWriter) {
      fileWriter.onwriteend = function () {
        resolve(fileEntry)
      }

      fileWriter.onerror = function (e) {
        reject(e)
      }
      fileWriter.write(dataObj)
    })
  })
}

// 读取本地提问历史记录
function readFile(fileEntry) {
  return new Promise((resolve, reject) => {
    fileEntry.file(function (file) {
      const reader = new FileReader()

      reader.onloadend = function () {
        resolve(this.result)
      }

      reader.readAsText(file)
    }, reject)
  })
}

// 自动保存提问历史记录
function autoSave() {
  // 创建历史记录文件
  createFile(historyDirEntry, userData.history).then(fileEntry => {
    // 写入历史记录文件
    writeFile(fileEntry, JSON.stringify(messages))
  })
}

// 添加复制功能
function addCopyBtn(div) {
  // 创建复制按钮
  const copyBtn = document.createElement('button')
  copyBtn.className = 'btn-copy'
  // 绑定复制事件
  copyBtn.addEventListener('click', () => {
    // 复制到剪切板
    const text = div.innerText
    cordova.plugins.clipboard.copy(text)
    alert('复制成功')
  })
  div.appendChild(copyBtn)
}

// 更新UI显示
function updateUI() {
  return new Promise((resolve, reject) => {
    settings.autoSave ? $('#autoSave').addClass('active') : $('#autoSave').removeClass('active')
    settings.useStream ? $('#useStream').addClass('active') : $('#useStream').removeClass('active')
    resolve()
  })
}

// 添加事件
function addEvent() {
  // 点击提交按钮
  $('#submit').click(questionSubmit)

  // 监听回车键
  $('textarea').bind('keydown', e => {
    if (e.keyCode == 13) {
      e.preventDefault()
      questionSubmit()
    }
  })

  // 监听输入框变化
  $('textarea').bind('input', e => {
    const question = $('textarea').val().trim()
    if (question == '') {
      $('#submit').attr('disabled', true)
    } else {
      $('#submit').removeAttr('disabled')
    }
  })

  // 点击清空
  $('#reload').click(() => {
    // 提醒用户是否清空
    confirm({
      content: `确认清空？`,
    }).then(() => {
      // 判断是否开启流式对话
      if (socket) {
        // 关闭socket
        socket.close()
        // 重置socket
        socket = null
        // 清空socket超时计时器
        clearInterval(socketTimeOut)
        isStreamStart = false
      }
      // 清空所有记录
      clearChat()
    })
  })

  // 切换自动保存
  $('#autoSave').bind('click', e => {
    e.preventDefault()
    // 判断是否开启了自动保存
    if (settings.autoSave) {
      // 提示用户关闭自动保存将会清空历史记录
      confirm(`确定不再记住？\n关闭后将清空历史记录`).then(() => {
        settings.autoSave = false
        // 清空所有记录
        clearChat().then(() => {
          // 保存设置
          saveSettings('autoSave', settings.autoSave).then(updateUI)
        })
      })
    } else {
      settings.autoSave = true
      // 保存设置
      saveSettings('autoSave', settings.autoSave).then(updateUI)
    }
  })

  // 切换对话模式
  $('#useStream').bind('click', e => {
    e.preventDefault()
    // 判断是否开启流式对话
    if (settings.useStream) {
      settings.useStream = false
      if (socket) {
        // 关闭socket
        socket.close()
        // 清空socket
        socket = null
        // 清空socket超时计时器
        clearInterval(socketTimeout)
        socketTimeout = null
      }
    } else {
      settings.useStream = true
    }
    // 保存设置
    saveSettings('useStream', settings.useStream).then(updateUI)
  })

  // 调整创造性
  $('.creativity input').bind('change', e => {
    settings.temperature = e.target.value
    // 保存设置
    saveSettings('temperature', settings.temperature)
    // 更新UI显示
    $('.creativity dfn').text(`创意度：${settings.temperature}`)
  })

  // 点击展开侧边栏
  $('.btn-menu').click(() => {
    $('menu').attr('open', 'open')

    // 监听侧边栏过渡动画结束事件
    $('menu').bind(
      'transitionend',
      () => {
        // 添加点击侧边栏以外区域关闭侧边栏事件
        $('body').bind('click', e => {
          if (e.target.tagName !== 'MENU') {
            $('menu').removeAttr('open')
            $('body').unbind('click')
          }
        })
      },
      { once: true }
    )
  })
}

// 保存设置
function saveSettings(key = 'default', value) {
  return new Promise((resolve, reject) => {
    if (key !== 'default') {
      settings[key] = value
    }
    // 写入设置文件
    createFile(appDirEntry, userData.settings).then(fileEntry => {
      // 写入设置文件
      writeFile(fileEntry, JSON.stringify(settings)).then(resolve).catch(reject)
    })
  })
}

// 后台自动更新应用
function backgroundUpdateApp() {
  return confirm(11111)
  // 获取服务器配置文件
  $()
    .ajax({
      url: `https://upload.pandorajs.com/apk/package.json?${Date.now()}`,
    })
    .then(res => {
      // 获取服务器版本号
      const { version, name: appName } = res
      // 获取本地版本号
      const { version: localVersion } = AppVersion
      // 判断是否有新版本
      if (version !== localVersion) {
        // 提示用户是否更新
        confirm(`有新版本，是否下载？`).then(() => {
          // 下载apk
          const fileTransfer = new FileTransfer()
          const uri = encodeURI(`https://upload.pandorajs.com/apk/${appName}.apk`)
          // 创建apk下载目录
          fileTransfer.download(
            uri,
            `${cordova.file.externalApplicationStorageDirectory}${appName}.apk`,
            () => {
              confirm(`下载完成，是否安装？`).then(() => {
                cordova.plugins.fileOpener2.open(`${cordova.file.externalApplicationStorageDirectory}${appName}.apk`, 'application/vnd.android.package-archive')
              })
            },
            () => {
              alert('更新失败，请稍后重试')
            },
            false
          )
        })
      } else {
        // 无须更新删除已下载的apk
        resolveLocalFileSystemURL(`${cordova.file.externalApplicationStorageDirectory}`, function (fileDir) {
          fileDir.getFile(`${appName}.apk`, { create: false, exclusive: true }, fileEntry => {
            fileEntry.remove()
          })
        })
      }
    })
}

// 初始化
function Init() {
  // 开始后台自动更新应用
  backgroundUpdateApp()

  resolveLocalFileSystemURL(cordova.file.externalApplicationStorageDirectory, function (dirEntry) {
    appDirEntry = dirEntry
    // 创建历史记录文件夹
    createDirectory(dirEntry, userData.history).then(dirEntry => {
      historyDirEntry = dirEntry
      // 读取历史记录文件
      historyDirEntry.getFile(userData.history, { create: false, exclusive: true }, fileEntry => {
        // 读取历史记录文件内容
        readFile(fileEntry).then(data => {
          messages = JSON.parse(data)
          messages.forEach(message => {
            const div = document.createElement('div')
            div.innerHTML = md.render(message.content)
            // 高亮html
            div.querySelectorAll('code').forEach(el => {
              hljs.highlightElement(el)
            })

            if (message.role == 'user') {
              div.className = 'right'
              $('article').append(div)
            } else {
              div.className = 'left'
              // 添加复制按钮
              addCopyBtn(div)
              $('article').append(div)
            }
          })

          //滚动到底部
          $('article').get.scrollTo({ top: $('article').get.scrollHeight })

          // 启用reload按钮
          $('#reload').removeAttr('disabled')
        })
      })
    })

    // 读取设置文件
    appDirEntry.getFile(
      userData.settings,
      { create: false, exclusive: true },
      fileEntry => {
        // 读取设置文件内容
        readFile(fileEntry).then(data => {
          const userSettings = JSON.parse(data)
          // 合并设置
          settings = Object.assign(settings, userSettings)
          updateUI()
          addEvent()
        })
      },
      () => {
        // 创建默认设置文件
        appDirEntry.getFile(userData.settings, { create: true, exclusive: false }, () => {
          saveSettings().then(() => {
            // 首次使用
            confirm({
              content: `欢迎使用问·想`,
              showCancel: false,
              confirmText: '进入',
            })

            updateUI()
            addEvent()
          })
        })
      }
    )
  })
}

switch (env) {
  default:
    document.addEventListener('deviceready', Init, false)
    break
  case 'web':
    Init()
    break
}

// TODO 收藏夹功能
// TODO 整合Midjourney
// TODO 角色扮演
// TODO 文本转语音
// TODO 分享功能
