import { memo, useEffect, useRef, useState } from "react"
import { Popover, type PopoverRenderPropArg } from "@headlessui/react"
import { useEvent } from "@aet/hooks"
import styled from "@emotion/styled"
import { cx } from "@emotion/css"
import { useTranslation } from "react-i18next"
import { sortBy } from "lodash"
import Icon from "@aet/icons/macro"
import { gql } from "~/generated/graphql"
import { FILE_FRAGMENT } from "~/options/fragments"
import { openFile } from "~/options/store/actions"
import { token } from "~/options/theme"
import { GET_FILES } from "./index"
import { removeTypeName } from "~/shared/removeTypeName"
import { useDispatch } from "~/options/store"
import { Input, PrimaryPill, SubheadingButton } from "~/shared/styled"
import { RuleType } from "~/shared/models/enums.generated"
import { useDataList } from "~/shared/hooks/useDatalist"
import { jaroDistance } from "./stringSimilarity"
import { useDomainSuggestions } from "~/options/hooks/useDomainSuggestions"
import { AddGlobalFileButton } from "./GlobalStyleAdder"
import { useMutate } from "~/shared/hooks/useMutate"

const CREATE_STYLE = gql<"CreateStyle">`
  ${FILE_FRAGMENT}
  mutation CreateStyle($input: PartialFileInput!) {
    newFile(input: $input) {
      ...FileFragment
    }
  }
`

export const AddStyle = () => (
  <Popover>{popoverProps => <AddStyleModel {...popoverProps} />}</Popover>
)

const AddStyleModel = memo<PopoverRenderPropArg>(({ open: isOpen, close }) => {
  const { t } = useTranslation()
  const dispatch = useDispatch()

  const createStyle = useMutate(CREATE_STYLE, {
    refetchQueries: [{ query: GET_FILES }],
  })

  const suggestions = useDomainSuggestions({ skip: !isOpen })
  const datalist = useDataList(suggestions, "hostname")

  const nameInputRef = useRef<HTMLInputElement>(null)
  const [domain, setDomain] = useState("")

  const onSetDomain = useEvent((e: React.ChangeEvent<HTMLInputElement>) => {
    const domain = e.target.value
    const suggestion = suggestions.find(s => s.hostname === domain)
    setDomain(domain)

    if (suggestion?.title && !nameInputRef.current?.value) {
      nameInputRef.current!.value = guessSiteName(suggestion.title, domain)
    }
  })

  useEffect(() => {
    if (isOpen) {
      nameInputRef.current?.focus()
    } else {
      setDomain("")
    }
  }, [isOpen])

  const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault()

    const { data } = await createStyle({
      input: {
        name: nameInputRef.current!.value,
        rules: [
          {
            value: domain,
            type: RuleType.Domain,
            exclude: false,
          },
        ],
      },
    })

    dispatch(openFile(removeTypeName(data!.newFile)))
    close()
  }

  return (
    <>
      <SubheadingButton
        as={Popover.Button}
        className={cx({ active: isOpen })}
        style={{ marginTop: -3 }}
        title={t("file.createStyle")}
      >
        <Icon icon="VscNewFile" />
      </SubheadingButton>

      <AddStyleModelContainer as={Popover.Panel}>
        <PopoverArrow />
        <form onSubmit={onSubmit}>
          <AddGlobalFileButton onClose={close} />
          <FormEntry>
            <FormLabel>{t("file.name")}</FormLabel>
            <Input required ref={nameInputRef} type="search" />
          </FormEntry>

          <FormEntry>
            <FormLabel>{t("rule.domain")}</FormLabel>
            <Input type="text" list={datalist.id} value={domain} onChange={onSetDomain} />
            {datalist.element}
          </FormEntry>

          <PrimaryPill type="submit">{t("editor.verbCreate")}</PrimaryPill>
        </form>
      </AddStyleModelContainer>
    </>
  )
})

function guessSiteName(title: string, hostname: string) {
  const separators = ["—", "–", "-"]

  // Remove TLD, and the first subdomain if it is shorter than 5 characters.
  const host = hostname.replace(/\.+$/, "").replace(/^[^.]{1,5}\.(.+)$/, "$1")

  for (const separator of separators) {
    if (!title.includes(separator)) {
      continue
    }

    return sortBy(
      title.split(separator).map(_ => _.trim()),
      text => jaroDistance(text, host),
    ).at(-1)!
  }

  return title || host
}

const AddStyleModelContainer = styled.form`
  background-color: ${token.canvas.overlay};
  border: 1px solid ${token.border.default};
  border-radius: 5px;
  box-shadow: ${token.shadow.extraLarge};
  list-style: none;
  padding-inline-start: 0;
  padding: 14px 15px;
  z-index: 5;

  position: absolute;
  right: 3px;
  margin-top: 7px;
`

const PopoverArrow = styled.div`
  position: absolute;
  width: 10px;
  height: 10px;
  background-color: ${token.canvas.overlay};
  transform: rotate(45deg);
  z-index: 1;
  top: -6px;
  right: 15px;
  border: 1px solid ${token.border.default};
  border-bottom: none;
  border-right: none;
`

const FormEntry = styled.div`
  margin-bottom: 13px;
`

const FormLabel = styled.label`
  display: block;
  margin-bottom: 8px;
  opacity: 0.8;
`
