import { memo, useEffect, useState } from "react"
import styled from "@emotion/styled"
import { useTranslation } from "react-i18next"
import { css, cx, keyframes } from "@emotion/css"
import toast from "react-hot-toast"
import Icon from "@aet/icons/macro"
import { useEvent } from "@aet/hooks"
import { gql, useMutation, useQuery } from "~/generated/graphql"
import { Modal } from "~/vendor/react-responsive-modal"
import { DangerPill, Input, Pill, PrimaryPill } from "~/shared/styled"
import { showErrorToast } from "~/shared/hooks/useShowApolloError"
import { FileInput } from "~/vendor/react-files-drag-and-drop"
import { useTreeContext } from "./index"
import { getFirstDomainRule } from "./Icon"

const IconModifierQuery = gql<"IconModifierQuery">`
  query IconModifierQuery($id: ID!) {
    file(id: $id) {
      id
      icon
      rules {
        type
        value
        exclude
      }
    }
  }
`

const IconModifierMutation = gql<"IconModifierMutation">`
  mutation IconModifierMutation($id: ID!, $icon: String) {
    setFile(id: $id, input: { icon: $icon }) {
      id
      icon
    }
  }
`

async function fetchIcoAsBase64(url: string) {
  const response = await fetch(url)
  const contentType = response.headers.get("Content-Type") || "image/x-icon"
  const arrayBuffer = await response.arrayBuffer()
  const base64String = window.btoa(
    new Uint8Array(arrayBuffer).reduce(
      (data, byte) => data + String.fromCharCode(byte),
      "",
    ),
  )
  return `data:${contentType};base64,${base64String}`
}

export const IconModifierModal = memo(() => {
  const { modifyingIconFileID: fileId, setModifyingIconFileID: setState } =
    useTreeContext()
  const onClose = useEvent(() => setState(null))
  const [loading, setLoading] = useState(false)

  const { t } = useTranslation()
  const [modified, setModified] = useState(false)
  const [url, setURL] = useState("")
  const [image, setImage] = useState<string | null>(null)

  useEffect(() => {
    if (!fileId) {
      setURL("")
      setImage(null)
      setModified(false)
    }
  }, [fileId])

  useQuery(IconModifierQuery, {
    variables: { id: fileId! },
    skip: !fileId,
    onError: showErrorToast,
    onCompleted({ file }) {
      setImage(file.icon || null)
      setModified(false)
      if (!url) {
        const domain = getFirstDomainRule(file.rules)
        if (domain) {
          setURL(domain)
        }
      }
    },
  })

  const [modifyIcon] = useMutation(IconModifierMutation, {
    onError: showErrorToast,
  })

  const onError = useEvent((message: string) => {
    toast.error(message)
  })

  const onUpload = useEvent(([file]: File[]) => {
    const reader = new FileReader()
    reader.onload = () => {
      setImage(reader.result as string)
      setModified(true)
    }
    reader.readAsDataURL(file)
  })

  const onSave = useEvent(async () => {
    if (!image || !fileId) return

    await modifyIcon({ variables: { id: fileId, icon: image } })
    onClose()
  })

  const onDelete = useEvent(async () => {
    if (!fileId) return

    await modifyIcon({ variables: { id: fileId, icon: null } })
    onClose()
  })

  const onDownloadFromURL = useEvent(async (_: unknown, path = url) => {
    if (!path) return

    try {
      setLoading(true)
      const base64 = await fetchIcoAsBase64(path)
      setModified(true)
      setImage(base64)
    } catch (e) {
      toast.error(e)
    } finally {
      setLoading(false)
    }
  })

  return (
    <Modal
      open={fileId != null}
      onClose={onClose}
      center
      showCloseIcon={false}
      classNames={{
        modal: cx(
          css`
            max-width: 310px;
            line-height: 1.45em;
          `,
          loading &&
            css`
              cursor: wait;
            `,
        ),
      }}
    >
      <Header>{t("editor.modifyIcon")}</Header>
      <FileInput
        onUpload={onUpload}
        onError={onError}
        extensions={[".png", ".jpg", ".jpeg", ".gif", ".svg"]}
        className={css`
          display: flex;
          align-items: center;
        `}
      >
        {({ dragging }) => (
          <div
            className={css`
              margin-top: 10px;
              margin-bottom: 20px;
              width: 100%;
              text-align: center;
            `}
          >
            {dragging ? <Hourglass /> : image ? <Img src={image} /> : <ImportIcon />}
          </div>
        )}
      </FileInput>

      <div
        className={css`
          display: flex;
          margin-bottom: 10px;
        `}
      >
        <Input
          value={url}
          onChange={useEvent(e => setURL(e.target.value))}
          placeholder="URL"
          onKeyUp={useEvent(e => {
            const { value } = e.currentTarget
            if (e.key === "Enter" && value) {
              void onDownloadFromURL(null, value)
            }
          })}
          className={css`
            flex: 1;
          `}
        />

        <DownloadPill
          disabled={!url || loading}
          title={t("editor.downloadIconFromURL")}
          onClick={onDownloadFromURL}
        >
          <Icon icon="FiDownload" />
        </DownloadPill>

        <DuckDuckGoPill
          disabled={!url || !/^([\w-]+\.)+\w+$/.test(url) || loading}
          title={t("editor.downloadIconFromDuckDuckGo")}
          onClick={useEvent(async () => {
            await onDownloadFromURL(null, `https://icons.duckduckgo.com/ip3/${url}.ico`)
          })}
        >
          <Icon icon="SiDuckduckgo" />
        </DuckDuckGoPill>
      </div>

      <div
        className={css`
          height: 20px;
        `}
      />

      <PrimaryButton tabIndex={0} onClick={onSave} disabled={!image || !modified}>
        {t("editor.verbSave")}
      </PrimaryButton>

      {image && (
        <DangerButton
          onClick={onDelete}
          tabIndex={0}
          className={css`
            margin-top: 7px;
          `}
        >
          {t("editor.verbRemoveIcon")}
        </DangerButton>
      )}

      <Button
        tabIndex={0}
        onClick={onClose}
        className={css`
          && {
            margin-top: 15px;
          }
        `}
      >
        {t("cancel")}
      </Button>
    </Modal>
  )
})

const Header = styled.h3`
  font-weight: 600;
  margin: 0;
  margin-bottom: 1em;
  text-align: center;
`

const Img = styled.img`
  max-width: 250px;
`

const Button = styled(Pill)`
  margin-top: 5px;
  width: 100%;
`

const PrimaryButton = styled(PrimaryPill)`
  width: 100%;
`
const DangerButton = styled(DangerPill)`
  width: 100%;
`

const rotation = keyframes`
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(359deg);
  }
`

const Hourglass = styled(Icon.of("BsHourglassTop"))`
  color: #5300ff;
  margin-right: 3px;
  font-size: 4.2em;
  animation: ${rotation} 2s infinite linear;
`

const fadeInOut = keyframes`
  0% {
    opacity: 0.4;
  }
  50% {
    opacity: 0.8;
  }
  100% {
    opacity: 0.4;
  }
`

const ImportIcon = styled(Icon.of("TbDragDrop"))`
  animation: ${fadeInOut} 3s ease-in-out infinite;
  font-size: 4.2em;
`

const DownloadPill = styled(Pill)`
  margin-left: 5px;
  && > svg {
    margin-right: -1px;
    font-size: 1.2em;
  }
`

const DuckDuckGoPill = styled(Pill)`
  color: #de5833;
  font-size: 1.4em;
  margin-left: 2px;
  && > svg {
    margin-right: -1px;
    margin-left: -1px;
  }
`
