import React from "react"
import ContentstackSDK from "@contentstack/app-sdk"

import { ImageElement } from "~/components/ImageElement"

import { Icon } from "@contentstack/venus-components"
import { openImageVault } from "~/utils/imagevault"

import type { Lang } from "~/types/lang"
import type {
  ContentstackEmbeddedData,
  ContentstackPluginDefinition,
  ExtractedContentstackEmbeddedData,
} from "~/types/contentstack"

function findThisPlugin(ext: ContentstackPluginDefinition) {
  return ext.type === "rte_plugin" && /imagevault/i.test(ext.title)
}

async function loadScript(url: string) {
  return new Promise((resolve, reject) => {
    if (!document) {
      throw new Error("Run in browser only")
    }

    const head = document.head || document.getElementsByTagName("head")[0]

    const script = document.createElement("script")
    script.type = "text/javascript"
    script.async = true
    script.src = url

    script.onload = function () {
      this.onerror = this.onload = null
      resolve(window.ImageVault)
    }

    script.onerror = function () {
      this.onerror = this.onload = null
      reject(`Failed to load ${this.src}`)
    }

    head.appendChild(script)
  })
}

function extractContentstackEmbeddedData(
  jwtLike: string
): ExtractedContentstackEmbeddedData | null {
  try {
    const base64str = jwtLike.replace(/-/g, "+").replace(/_/g, "/")
    const jsonStr = decodeURIComponent(
      window
        .atob(base64str)
        .split("")
        .map(function (c) {
          return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)
        })
        .join("")
    )

    const json = JSON.parse(jsonStr)
    json.exports = JSON.parse(json.exports)
    json.props.value = JSON.parse(json.props.value)

    if (IS_DEV) {
      console.log(`Contentstack Embedded Data`, json)
    }

    const {
      entryMetadata,
      utilis: {
        content_type: { schema },
        extensions,
      },
      requestProps: {
        stack: { api_key },
        branch,
      },
    }: ContentstackEmbeddedData = json.props.value

    const titleField = schema.find((f) => f.uid === "title")
    // We force this value with ! because we know it exists.
    // Otherwise this code would not run.
    const plugin = extensions.find(findThisPlugin)!

    return {
      stack: {
        apiKey: api_key,
      },
      branch,
      contentType: {
        uid: entryMetadata.contentTypeUid,
      },
      entry: {
        locale: entryMetadata.locale,
        title:
          titleField && titleField.data_type === "text"
            ? titleField.value
            : "Untitled",
        uid: entryMetadata.entryUid,
      },
      plugin,
    }
  } catch (e) {
    console.log(`Unable to parse JWT like: ${jwtLike}`)
  }
  return null
}

let ivloaded = false
function loadIV(plugin: ContentstackPluginDefinition) {
  if (plugin.src) {
    const url = new URL(plugin.src)
    url.pathname = "/scripts/imagevault-insert-media/insertmediawindow.min.js"
    if (IS_DEV) {
      console.log(`Loading script: ${url.toString()}`)
    }
    loadScript(url.toString())
    ivloaded = true
  }
}

async function init() {
  try {
    const sdk = await ContentstackSDK.init()

    const extensionObj = sdk?.location
    const RTE = extensionObj?.RTEPlugin

    let embeddedData: ExtractedContentstackEmbeddedData | null = null
    const jwtLike = window.name.split("__").find((str) => str.startsWith("ey"))
    if (jwtLike) {
      embeddedData = extractContentstackEmbeddedData(jwtLike)
      if (embeddedData?.plugin) {
        loadIV(embeddedData.plugin)
      }
    }

    if (!RTE) return

    const ImageVault = RTE("ImageVault", (rte) => {
      if (rte) {
        if (!ivloaded) {
          // Loading failed via embedded data above, try again with data inside RTE
          // @ts-expect-error: incorrect typings, requestProps is available at runtime
          const extensions = rte._adv.editor.requestProps
            .extensions as ContentstackPluginDefinition[]
          const plugin = extensions.find(findThisPlugin)

          if (plugin) {
            loadIV(plugin)
          }
        }
      }

      return {
        title: "Choose assets from ImageVault",
        icon: <Icon icon="Image" version="v2" height={24} width={24} />,
        render: (props) => {
          if (rte) {
            return (
              <ImageElement element={props.element} rte={rte}>
                {props.children}
              </ImageElement>
            )
          } else {
            console.error("No instance of RTE found")
            return (
              <p>An unexpected error occured, see console for more info.</p>
            )
          }
        },
        display: ["toolbar"],
        elementType: ["void"],
      }
    })

    ImageVault.on("exec", async (rte) => {
      if (rte) {
        const savedSelection = rte?.selection?.get() ?? undefined

        // @ts-expect-error: Added at runtime
        const appConfig = await rte.getConfig()

        // This is the configuration for this instance of the plugin.
        // You edit this in the content types settings RTE plugin section.
        // @ts-expect-error: Added at runtime
        const pluginConfig = await rte.getFieldConfig()

        const config = {
          ...appConfig,
          ...pluginConfig,
        }

        openImageVault({
          config,
          entryData: {
            //TODO: Add support for branches
            branch: embeddedData ? embeddedData.contentType.uid : "main",
            contentTypeUid: embeddedData
              ? embeddedData.contentType.uid
              : "Unknown",
            locale: embeddedData
              ? embeddedData.entry.locale
              : ("unknown" as Lang),
            stackApiKey: embeddedData ? embeddedData.stack.apiKey : "unknown",
            title: embeddedData ? embeddedData.entry.title : "Untitled",
            uid: embeddedData ? embeddedData.entry.uid : "unknown",
          },
          onSuccess(result) {
            rte.insertNode(
              {
                // @ts-expect-error: incorrect typings
                type: "ImageVault",
                attrs: result,
                uid: crypto.randomUUID(),
                children: [{ text: "" }],
              },
              { at: savedSelection }
            )
          },
        })
      }
    })
    return {
      ImageVault,
    }
  } catch (e) {
    console.error({ e })
  }
}

export default init()
