import { app } from '../../../scripts/app.js'
import { $el } from '../../../scripts/ui.js'
import { api } from '../../../scripts/api.js'

import { td_bg } from './td_background.js'
// console.log('td_bg', td_bg)
import {
  getUrl,
  base64Df,
  get_position_style,
  getObjectInfo
} from './common.js'

//本机安装的插件节点全集
window._nodesAll = null

const parseImageToBase64 = url => {
  return new Promise((res, rej) => {
    fetch(url)
      .then(response => response.blob())
      .then(blob => {
        const reader = new FileReader()
        reader.onloadend = () => {
          const base64data = reader.result
          res(base64data)
          // 在这里可以将base64数据用于进一步处理或显示图片
        }
        reader.readAsDataURL(blob)
      })
      .catch(error => {
        console.log('发生错误:', error)
      })
  })
}

async function drawImageToCanvas (imageUrl, sFactor = 320) {
  var canvas = document.createElement('canvas')
  var ctx = canvas.getContext('2d')
  var img = new Image()

  await new Promise((resolve, reject) => {
    img.onload = function () {
      var scaleFactor = sFactor / img.width
      var canvasWidth = img.width * scaleFactor
      var canvasHeight = img.height * scaleFactor

      canvas.width = canvasWidth
      canvas.height = canvasHeight

      ctx.drawImage(img, 0, 0, canvasWidth, canvasHeight)

      resolve()
    }

    img.onerror = function () {
      reject(new Error('Failed to load image'))
    }

    img.src = imageUrl
  })

  var base64 = canvas.toDataURL('image/jpeg')
  // console.log(base64); // 输出Base64数据
  return base64
  // 可以在这里执行其他操作，比如将Base64数据保存到服务器或显示在页面上
}

async function extractInputAndOutputData (
  jsonData,
  inputIds = [],
  outputIds = []
) {
  // workflow
  // const workflow=jsonData.workflow;
  // const nodes=workflow.nodes;

  const data = jsonData.output
  let input = []
  let output = []
  const seed = {}
  const seedTitle = {}

  for (const id in data) {
    if (data.hasOwnProperty(id)) {
      let node = app.graph.getNodeById(id)
      if (inputIds.includes(id)) {
        // let node = app.graph.getNodeById(id)
        let options = {}
        // 模型
        try {
          if (node.type === 'CheckpointLoaderSimple') {
            options = node.widgets.filter(w => w.name === 'ckpt_name')[0]
              .options.values
          } else if (node.type === 'LoraLoader') {
            options = node.widgets.filter(w => w.name === 'lora_name')[0]
              .options.values
          }
        } catch (error) {}

        if (node.type == 'IntNumber' || node.type == 'FloatSlider') {
          // min max step
          let [v, min, max, step] = Array.from(node.widgets, w => w.value)
          options = { min, max, step }
          // node.widgets.filter(w => w.type === 'number')[0].options
        }

        if (node.type == 'PromptSlide') {
          // min max step
          options = node.widgets.filter(w => w.type === 'slider')[0].options
          // 备选的keywords清单
          try {
            let keywords = node.widgets.filter(w => w.name === 'upload')[0]
              .value
            keywords = JSON.parse(keywords)
            options.keywords = keywords
          } catch (error) {
            console.log(error)
          }
        }

        if (node.type == 'ImagesPrompt_') {
          //图库
          // console.log('ImagesPrompt_', data[id])
          let image_base64 = data[id].inputs.image_base64
          let img_index = 0
          let imgsData = JSON.parse(data[id].inputs.upload)
          for (let index = 0; index < imgsData.length; index++) {
            const imgd = imgsData[index].imgurl
            imgsData[index].index = index
            //TODO缩放大小
            imgsData[index].imgurl = await parseImageToBase64(imgd)
            if (image_base64 == imgsData[index].imgurl) {
              img_index = index
            }
          }
          options.images = imgsData
          delete data[id].inputs.upload
          delete data[id].inputs.image_base64

          data[id].inputs.imageIndex = img_index
        }

        if (node.type == 'Color') {
        }

        // 语音输入的支持
        if (node.type == 'LoadAndCombinedAudio_') {
          // if (
          //   data[id].widgets_values &&
          //   data[id].widgets_values[0] &&
          //   data[id].widgets_values[0].base64 &&
          //   data[id].widgets_values[0].base64.length > 0
          // ) {
          //   options.defaultBase64 = data[id].widgets_values[0].base64
          // }

          input[inputIds.indexOf(id)] = {
            ...data[id],
            title: node.title,
            id,
            options
          }
        }

        if (node.type === 'LoadImage') {
          // loadImage的mask支持
          let output = node.outputs.filter(ot => ot.type == 'MASK')[0]
          if (output.links) {
            // 有输出
            options.hasMask = true
          }
          // loadImage的默认图，转为base64
          let imgurl = app.graph.getNodeById(id).imgs[0].src + '&channel=rgb'

          options.defaultImage = await drawImageToCanvas(imgurl, 512)
          console.log('#loadImage的默认图', options)
        }

        input[inputIds.indexOf(id)] = {
          ...data[id],
          title: node.title,
          id,
          options
        }
        // input.push()
      }
      if (outputIds.includes(id)) {
        let options = {}
        //输出的默认图
        if (
          node.type === 'SaveImageAndMetadata_' &&
          app.graph.getNodeById(id).imgs
        ) {
          // SaveImageAndMetadata_的默认图，转为base64
          let imgurl = app.graph.getNodeById(id).imgs[0].src

          options.defaultImage = await drawImageToCanvas(imgurl, 512)
          console.log('#SaveImageAndMetadata_的默认图', options)
        }

        // let node = app.graph.getNodeById(id)
        // output.push()
        output[outputIds.indexOf(id)] = {
          ...data[id],
          title: node.title,
          id,
          options
        }
      }

      if (
        node.type === 'KSampler' ||
        node.type == 'SamplerCustom' ||
        node.type === 'ChinesePrompt_Mix' ||
        node.type === 'Seed_' ||
        node.type === 'SiliconflowLLM' ||
        node.type === 'ChatGPTOpenAI' ||
        node.type === 'SiliconflowTextToImageNode'
      ) {
        // seed 的类型收集
        try {
          seed[id] = node.widgets.filter(
            w => w.name === 'seed' || w.name == 'noise_seed'
          )[0].linkedWidgets[0].value
          seedTitle[id] = node.title
        } catch (error) {}
      }
    }
  }

  // 修复bug，当节点不存在时
  input = input.filter(i => i)
  output = output.filter(i => i)

  return { input, output, seed, seedTitle }
}

async function save_app (json) {
  let url = getUrl()

  const res = await fetch(`${url}/mixlab/workflow`, {
    method: 'POST',
    body: JSON.stringify({
      data: json,
      task: 'save_app',
      filename: json.app.filename,
      category: json.app.category
    })
  })
  return await res.json()
}

function downloadJsonFile (jsonData, fileName = 'mix_app.json') {
  const dataString = JSON.stringify(jsonData)
  const blob = new Blob([dataString], { type: 'application/json' })
  const url = URL.createObjectURL(blob)

  const link = document.createElement('a')
  link.href = url
  link.download = fileName
  link.click()

  // 释放URL对象
  setTimeout(() => {
    URL.revokeObjectURL(url)
  }, 0)
}

async function save (json, download = false, showInfo = true) {
  if (!window._nodesAll) {
    window._nodesAll = await getObjectInfo();
  }

  let nodesAll = window._nodesAll;
  
  // let nodesAll = window._nodesAll || (await getObjectInfo())

  console.log('####SAVE', nodesAll, json)

  const name = json[0],
    version = json[5],
    share_prefix = json[6], //用于分享的功能扩展
    link = json[7], //用于创建界面上的跳转链接
    category = json[8] || '', //用于分类
    idle_animation = json[9], //用于动画，比如数字人her
    description = json[4],
    inputIds = json[2].split('\n').filter(f => f),
    outputIds = json[3].split('\n').filter(f => f)

  const iconData = json[1][0]

  let { filename, subfolder, type } = iconData
  let iconUrl = api.apiURL(
    `/view?filename=${encodeURIComponent(
      filename
    )}&type=${type}&subfolder=${subfolder}${app.getPreviewFormatParam()}${app.getRandParam()}`
  )

  try {
    let data = await app.graphToPrompt()

    //从output数据里把工作流的节点，插件数据统计出来
    data.nodesMap = {}
    for (const id in data.output) {
      data.nodesMap[data.output[id].class_type] =
        nodesAll[data.output[id].class_type]
    }

    let { input, output, seed, seedTitle } = await extractInputAndOutputData(
      data,
      inputIds,
      outputIds
    )

    let authorAvatar =
        localStorage.getItem('_mixlab_author_avatar') || base64Df,
      authorName =
        localStorage.getItem('_mixlab_author_name') ||
        localStorage.getItem('Comfy.userName'),
      authorLink = localStorage.getItem('_mixlab_author_link') || ''

    data.app = {
      name,
      description,
      version,
      input,
      output,
      seed, //控制是fixed 还是random
      seedTitle,
      share_prefix,
      link,
      category,
      filename: `${name}_${version}.json`,
      author: {
        avatar: authorAvatar,
        name: authorName,
        link: authorLink
      }
    }

    try {
      data.app.icon = await drawImageToCanvas(iconUrl)
    } catch (error) {}

    let images = []
    if (json[1].length > 1 && idle_animation) {
      images = Array.from(json[1], j => {
        let { filename, subfolder, type } = j
        return api.apiURL(
          `/view?filename=${encodeURIComponent(
            filename
          )}&type=${type}&subfolder=${subfolder}${app.getPreviewFormatParam()}${app.getRandParam()}`
        )
      })
    }

    try {
      for (let index = 0; index < images.length; index++) {
        const imgurl = images[index]
        images[index] = await drawImageToCanvas(imgurl)
      }
      if (idle_animation) data.app.idle_animation = images
    } catch (error) {}

    // console.log(data.app)
    // let http_workflow = app.graph.serialize()
    await save_app(data)
    if (download) {
      await downloadJsonFile(data, data.app.filename)
    }

    if (showInfo) {
      let open = window.confirm(
        `You can now access the standalone application on a new page!\n${getUrl()}/mixlab/app${
          data.app.idle_animation ? '/her.html' : ''
        }?filename=${encodeURIComponent(
          data.app.filename
        )}&category=${encodeURIComponent(data.app.category)}`
      )
      if (open)
        window.open(
          `${getUrl()}/mixlab/app${
            data.app.idle_animation ? '/her.html' : ''
          }?filename=${encodeURIComponent(
            data.app.filename
          )}&category=${encodeURIComponent(data.app.category)}`
        )
    }
  } catch (error) {
    console.log('###error', error)
  }
}

function getInputsAndOutputs () {
  const inputs =
      `LoadImage LoadImagesToBatch ImagesPrompt_ LoadAndCombinedAudio_ LoadVideoAndSegment_ VHS_LoadVideo CLIPTextEncode PromptSlide TextInput_ Color FloatSlider IntNumber CheckpointLoaderSimple LoraLoader`.split(
        ' '
      ),
    outputs =
      `SaveTripoSRMesh,PreviewImage,SaveImage,TransparentImage,ShowTextForGPT,CombineAudioVideo,VHS_VideoCombine,VideoCombine_Adv,Image Save,SaveImageAndMetadata_,ClipInterrogator`.split(
        ','
      )

  let inputsId = [],
    outputsId = []

  for (let node of app.graph._nodes) {
    if (inputs.includes(node.type)) {
      inputsId.push(node.id)
    }

    if (outputs.includes(node.type)) {
      outputsId.push(node.id)
    }
  }

  return {
    input: inputsId,
    output: outputsId
  }
}

app.registerExtension({
  name: 'Mixlab.utils.AppInfo',
  init () {
    // if (!window._nodesAll) {
    //   getObjectInfo().then(r => (window._nodesAll = r))
    // }
  },
  async beforeRegisterNodeDef (nodeType, nodeData, app) {
    if (nodeType.comfyClass == 'AppInfo') {
      const orig_nodeCreated = nodeType.prototype.onNodeCreated
      nodeType.prototype.onNodeCreated = function () {
        orig_nodeCreated?.apply(this, arguments)
        // console.log('#orig_nodeCreated', this)

        // 自动计算workflow里哪些节点支持
        let input_ids = this.widgets.filter(w => w.name == 'input_ids')[0],
          output_ids = this.widgets.filter(w => w.name == 'output_ids')[0]

        const { input, output } = getInputsAndOutputs()
        input_ids.value = input.join('\n')
        output_ids.value = output.join('\n')
        const widget = {
          type: 'div',
          name: 'AppInfoRun',
          draw (ctx, node, widget_width, y, widget_height) {
            Object.assign(this.div.style, {
              ...get_position_style(
                ctx,
                widget_width,
                node.size[1] - widget_height,
                node.size[1]
              ),
              zIndex: 1
            })
          }
        }

        const style = `
        flex-direction: row;
        background-color: var(--comfy-input-bg);
        border-radius: 8px;
        border-color: var(--border-color);
        border-style: solid;
        color: var(--descrip-text);`

        widget.div = $el('div', {})

        const btn = document.createElement('button')
        btn.innerText = 'Save & Open'
        btn.style = style

        btn.addEventListener('click', () => {
          //   console.log('hahhah')
          if (window._mixlab_app_json) {
            save(window._mixlab_app_json)
          } else {
            alert('Please run the workflow before saving')
            // app.queuePrompt(0, 1)
            this.widgets.filter(w => w.name === 'version')[0].value += 1
          }
        })

        const download = document.createElement('button')
        download.innerText = 'Download For App'
        download.style = style
        download.style.marginLeft = '12px'

        download.addEventListener('click', () => {
          //   console.log('hahhah')
          if (window._mixlab_app_json) {
            save(window._mixlab_app_json, true)
          } else {
            alert('Please run the workflow before saving')
            // app.queuePrompt(0, 1)
            this.widgets.filter(w => w.name === 'version')[0].value += 1
          }
        })

        //td bg
        const tdBG = document.createElement('button')
        tdBG.innerText = 'Canvas Mode'
        tdBG.style = style
        tdBG.style.marginLeft = '12px'

        tdBG.addEventListener('click', () => {
          td_bg.toggle()
          if (td_bg.running) {
            tdBG.style.background = 'yellow'
          } else {
            tdBG.style.background = 'transparent'
          }
        })

        // author
        let author = document.createElement('div')
        // author.style=`display: flex`

        let authorAvatar = document.createElement('img')
        authorAvatar.className = `${'comfy-multiline-input'}`
        authorAvatar.style = `outline: none;
          border: none;
          padding: 4px;
          width: 32px;
          cursor: pointer;
          height: 32px;`

        if (localStorage.getItem('_mixlab_author_avatar')) {
          authorAvatar.src =
            localStorage.getItem('_mixlab_author_avatar') || base64Df
        }

        let authorAvatarUpload = document.createElement('input')
        authorAvatarUpload.type = 'file'
        authorAvatarUpload.style = `display:none`

        let authorAvatarInput = document.createElement('div')
        authorAvatarInput.style = `display: flex;justify-content: flex-start;
        align-items: center;`
        let authorAvatarInputLabel = document.createElement('p')
        authorAvatarInputLabel.innerText = 'Author Avatar'
        authorAvatarInputLabel.className = `${'comfy-multiline-input'}`
        authorAvatarInputLabel.style = `font-size:12px`

        authorAvatar.addEventListener('click', e => {
          authorAvatarUpload.click()
        })

        authorAvatarInputLabel.addEventListener('click', e => {
          authorAvatarUpload.click()
        })

        authorAvatarUpload.addEventListener('change', event => {
          const file = event.target.files[0]
          const reader = new FileReader()

          reader.onload = async e => {
            let im = new Image()
            im.src = e.target.result
            authorAvatar.src = e.target.result
            im.onload = () => {
              let c = document.createElement('canvas')
              let ctx = c.getContext('2d')
              c.width = 72
              c.height = 72
              ctx.drawImage(
                im,
                0,
                0,
                im.naturalWidth,
                im.naturalHeight,
                0,
                0,
                c.width,
                c.height
              )
              window._mixlab_author_avatar = c.toDataURL()
              localStorage.setItem(
                '_mixlab_author_avatar',
                window._mixlab_author_avatar
              )
            }
          }

          // 以文本形式读取文件
          reader.readAsDataURL(file)
        })

        author.appendChild(authorAvatarInput)
        authorAvatarInput.appendChild(authorAvatarInputLabel)
        authorAvatarInput.appendChild(authorAvatar)
        authorAvatarInput.appendChild(authorAvatarUpload)

        let authorName = document.createElement('input')
        authorName.type = 'text'
        authorName.value =
          localStorage.getItem('_mixlab_author_name') ||
          localStorage.getItem('Comfy.userName')
        authorName.placeholder = 'author name'
        authorName.className = `${'comfy-multiline-input'}`
        authorName.style = `
          outline: none;
          border: none;
          padding: 4px;
          width: 100%;
          cursor: pointer;
          height: 32px;`

        let authorNameInput = document.createElement('div')
        authorNameInput.style = `display: flex;justify-content: flex-start;
          align-items: center;`
        let authorNameInputLabel = document.createElement('p')
        authorNameInputLabel.innerText = 'Author Name'
        authorNameInputLabel.className = `${'comfy-multiline-input'}`
        authorNameInputLabel.style = `font-size:12px;width: 110px`

        authorName.addEventListener('change', e => {
          window._mixlab_author_name = authorName.value.trim()
          localStorage.setItem(
            '_mixlab_author_name',
            window._mixlab_author_name
          )
        })

        author.appendChild(authorNameInput)
        authorNameInput.appendChild(authorNameInputLabel)
        authorNameInput.appendChild(authorName)

        // 社交链接
        let authorLink = document.createElement('input')
        authorLink.type = 'text'
        authorLink.value = localStorage.getItem('_mixlab_author_link') || ''
        authorLink.placeholder = 'author link'
        authorLink.className = `${'comfy-multiline-input'}`
        authorLink.style = `
          outline: none;
          border: none;
          padding: 4px;
          width: 100%;
          cursor: pointer;
          height: 32px;`

        let authorLinkInput = document.createElement('div')
        authorLinkInput.style = `display: flex;justify-content: flex-start;
          align-items: center;`
        let authorLinkInputLabel = document.createElement('p')
        authorLinkInputLabel.innerText = 'Author Link'
        authorLinkInputLabel.className = `${'comfy-multiline-input'}`
        authorLinkInputLabel.style = `font-size:12px;width: 110px`

        authorLink.addEventListener('change', e => {
          window._mixlab_author_link = authorLink.value.trim()
          localStorage.setItem(
            '_mixlab_author_link',
            window._mixlab_author_link
          )
        })

        author.appendChild(authorLinkInput)
        authorLinkInput.appendChild(authorLinkInputLabel)
        authorLinkInput.appendChild(authorLink)

        widget.div.appendChild(author)

        let btns = document.createElement('div')

        widget.div.appendChild(btns)

        btns.appendChild(btn)
        btns.appendChild(download)
        btns.appendChild(tdBG)

        document.body.appendChild(widget.div)
        this.addCustomWidget(widget)

        const onRemoved = this.onRemoved
        this.onRemoved = () => {
          widget.div.remove()
          return onRemoved?.()
        }

        this.serialize_widgets = true //需要保存参数

        window._mixlab_app_json = null
      }

      const onExecuted = nodeType.prototype.onExecuted
      nodeType.prototype.onExecuted = function (message) {
        onExecuted?.apply(this, arguments)
        console.log(message.json)
        window._mixlab_app_json = message.json
        try {
          let a = this.widgets.filter(w => w.name === 'AppInfoRun')[0]
          if (a) {
            if (!a.value) a.value = 0
            a.value += 1
          }

          const div = this.widgets.filter(w => w.div)[0].div
          Array.from(div.querySelectorAll('button'), b =>
            b.innerText != 'Canvas Mode' ? (b.style.background = 'yellow') : ''
          )
        } catch (error) {}
      }
    }
  },
  async loadedGraphNode (node, app) {
    // console.log('#loadedGraphNode1111')
    window._mixlab_app_json = null //切换workflow需要清空
    if (node.type === 'AppInfo') {
      let auto_save = node.widgets.filter(w => w.name == 'auto_save')[0]
      if (auto_save) {
        if (!['enable', 'disable'].includes(auto_save.value)) {
          auto_save.value = 'enable'
        }
      }

      // app.canvas.centerOnNode(node)
      // app.canvas.setZoom(0.45)
    }
  }
})

api.addEventListener('execution_start', async ({ detail }) => {
  console.log('#execution_start', detail)
  window._mixlab_app_json = null
})

api.addEventListener('executed', async ({ detail }) => {
  console.log('#executed', detail)
  // window._mixlab_app_json=null;
  const { output } = getInputsAndOutputs()
  if (output.includes(parseInt(detail.node))) {
    let appinfo = app.graph.findNodesByType('AppInfo')[0]
    if (appinfo) {
      let auto_save = appinfo.widgets.filter(w => w.name == 'auto_save')[0]
      if (auto_save?.value === 'enable') {
        // 自动保存
        console.log('auto_save')
        if (window._mixlab_app_json) save(window._mixlab_app_json, false, false)
      }
    }
  }
})
