import styled from "@emotion/styled"
import { css } from "@emotion/css"
import { useAutoCallback } from "hooks.macro"
import { memo, useCallback, useEffect, useRef, useState } from "react"
import { useTranslation } from "react-i18next"
import Icon from "@aet/icons/macro"
import { toast } from "react-hot-toast"
import { useEvent } from "@aet/hooks"
import { gql, useApolloClient, useMutation } from "~/generated/graphql"
import type * as types from "~/generated/graphql"
import { Select } from "./LocalePicker"
import { FormEntry, Input } from "./styled"
import { ButtonWrapper } from "~/shared/styled"
import { token } from "~/options/theme"

export const PREFERENCES_FRAGMENT = gql`
  fragment PreferencesFragment on Preferences {
    darkTheme
    debugMode
    enabled
    faviconServer
    fontFamily
    fontSize
    formatOnSave
    lightTheme
    lineHeight
    minimap
    recordUsageData
    showBadge
    tabSize
    theme
  }
`

const UPDATE_PREFERENCES = gql<"UpdatePreferences">`
  ${PREFERENCES_FRAGMENT}
  mutation UpdatePreferences($preferences: InputPreferences!) {
    setPreferences(preferences: $preferences) {
      ...PreferencesFragment
    }
  }
`

const RESET_USAGE_DATA = gql<"ResetUsageData">`
  mutation ResetUsageData {
    resetUsageData
  }
`

const DelayedInput: React.FC<{
  type: string
  value: string
  onChange: (value: string) => void
}> = ({ type, value, onChange }) => {
  const [inputValue, setInputValue] = useState(value)
  const timeout = useRef<number>()

  useEffect(() => {
    if (timeout.current) {
      clearTimeout(timeout.current)
    }
    timeout.current = window.setTimeout(() => {
      onChange(inputValue)
    }, 400)
  }, [inputValue, onChange])

  return (
    <Input
      type={type}
      value={inputValue}
      onChange={useEvent(e => {
        setInputValue(e.target.value)
      })}
    />
  )
}

export const Preferences: FC<ClassNameProp & { data: types.SettingsTabQueryQuery }> = ({
  data,
}) => {
  const { t } = useTranslation()
  const [updatePreferences] = useMutation(UPDATE_PREFERENCES)
  const update = useAutoCallback((fragment: types.InputPreferences) => {
    void updatePreferences({ variables: { preferences: fragment } })
  })

  return (
    <>
      <FormEntry>
        <label>{t("preferences.colorScheme")}</label>
        <Select
          as="select"
          value={data.preferences.theme}
          className={css`
            padding: 6px 12px;
          `}
          onChange={useEvent(e => {
            update({ theme: e.target.value as types.Theme })
          })}
        >
          <option value="Dark">🌑 {t("preferences.darkMode")}</option>
          <option value="Light">☀️ {t("preferences.lightMode")}</option>
          <option value="System">🌗 {t("preferences.autoDetect")}</option>
        </Select>
      </FormEntry>

      <FormEntry>
        <label>{t("preferences.darkTheme")}</label>
        <Select
          as="select"
          value={data.preferences.darkTheme}
          className={css`
            padding: 6px 12px;
          `}
          onChange={useEvent(e => {
            update({ darkTheme: e.target.value as types.Theme })
          })}
        >
          <option value="github-dark">GitHub Dark</option>
          <option value="github-dark-default">GitHub Dark Default</option>
        </Select>
      </FormEntry>

      <FormEntry>
        <label>{t("preferences.lightTheme")}</label>
        <Select
          as="select"
          value={data.preferences.lightTheme}
          className={css`
            padding: 6px 12px;
          `}
          onChange={useEvent(e => {
            update({ lightTheme: e.target.value as types.Theme })
          })}
        >
          <option value="github-light">GitHub Light</option>
          <option value="github-light-default">GitHub Light Default</option>
        </Select>
      </FormEntry>

      <FormEntry>
        <label>{t("preferences.fontFamily")}</label>
        <DelayedInput
          type="text"
          value={data.preferences.fontFamily}
          onChange={useEvent(fontFamily => {
            update({ fontFamily })
          })}
        />
      </FormEntry>

      <FormEntry>
        <label>{t("preferences.fontSize")}</label>
        <Input
          type="number"
          value={data.preferences.fontSize}
          onChange={useEvent(e => {
            update({ fontSize: parseInt(e.target.value) })
          })}
        />
      </FormEntry>

      <FormEntry>
        <label>{t("preferences.lineHeight")}</label>
        <Input
          type="number"
          value={data.preferences.lineHeight}
          onChange={useEvent(e => {
            update({ lineHeight: parseInt(e.target.value) })
          })}
        />
      </FormEntry>

      <FormEntry>
        <FormEntryLine>
          <input
            type="checkbox"
            checked={data.preferences.formatOnSave}
            onChange={useEvent(e => {
              update({ formatOnSave: e.target.checked })
            })}
          />
          <label
            className={css`
              margin-left: 8px;
            `}
          >
            {t("preferences.formatOnSave")}
          </label>
        </FormEntryLine>
      </FormEntry>

      <FormEntry>
        <FormEntryLine>
          <input
            type="checkbox"
            checked={data.preferences.minimap}
            onChange={useEvent(e => {
              update({ minimap: e.target.checked })
            })}
          />
          <label
            className={css`
              margin-left: 8px;
            `}
          >
            {t("preferences.minimap")}
          </label>
        </FormEntryLine>
      </FormEntry>

      <FormEntry>
        <FormEntryLine>
          <input
            type="checkbox"
            checked={data.preferences.debugMode}
            onChange={useEvent(e => {
              update({ debugMode: e.target.checked })
            })}
          />
          <label
            className={css`
              margin-left: 8px;
            `}
          >
            {t("preferences.debugMode")}
          </label>
        </FormEntryLine>
      </FormEntry>

      <UsageDataPreference
        enabled={data.preferences.recordUsageData}
        onChange={useEvent(value => {
          update({ recordUsageData: value })
        })}
      />
    </>
  )
}

const UsageDataPreference = memo<{
  enabled: boolean
  onChange: (value: boolean) => void
}>(({ enabled, onChange }) => {
  const { t } = useTranslation()
  const client = useApolloClient()

  const resetUsageData = useCallback(async () => {
    await client.mutate({ mutation: RESET_USAGE_DATA })
    const { cache } = client

    // https://github.com/apollographql/apollo-client/issues/4724#issuecomment-487373566
    const ids = Object.values(cache.extract(true))
      .filter((value: any) => value.__typename === "File")
      .map(value => cache.identify(value))
      .filter(Boolean)

    ids.forEach(id => cache.evict({ id, fieldName: "lastRequestedAt" }))

    toast.success(t("preferences.resetUsageDataCompleted"))
  }, [client, t])

  return (
    <FormEntry>
      <FormEntryLine>
        <input
          type="checkbox"
          checked={enabled}
          onChange={useEvent(e => {
            onChange(e.target.checked)
          })}
        />
        <label
          className={css`
            flex: 1;
            margin-left: 8px;
          `}
        >
          {t("preferences.recordUsageData")}
        </label>

        <ButtonWrapper
          onClick={() => resetUsageData()}
          title={t("preferences.resetUsageData")}
          className={css`
            cursor: pointer;
            font-size: 1.1em;
            &:hover {
              color: ${token.danger.emphasis};
              transition: 0.2s;
            }
          `}
        >
          <Icon icon="VscTrash" />
        </ButtonWrapper>
      </FormEntryLine>
      <div
        className={css`
          margin-top: 5px;
          opacity: 0.7;
          margin-left: 21px;
          font-size: 0.9em;
          line-height: 1.3em;
        `}
      >
        {t("preferences.recordUsageDataDescription")}
      </div>
    </FormEntry>
  )
})

const FormEntryLine = styled.div`
  display: flex;
  align-items: center;
`
