import {
  Accordion,
  ActionIcon,
  Badge,
  Box,
  Button,
  Card,
  Code,
  CopyButton,
  Flex,
  HoverCard,
  Input,
  Modal,
  Tooltip,
  Typography
} from '@tidbcloud/uikit'
import { Form, FormTextInput, FormSelect, ProTable, MRT_ColumnDef } from '@tidbcloud/uikit/biz'
import {
  IconChevronRightFill,
  IconCopy01,
  IconEdit05,
  IconInfoCircle,
  IconRefreshCw02,
  IconTrash01
} from '@tidbcloud/uikit/icons'
import { useEffect, useMemo, useState } from 'react'
import { useForm } from 'react-hook-form'

import {
  useCreateCustomDomain,
  useEditCustomDomain,
  useGetCustomDomains,
  useDeleteCustomDomain
} from 'dbaas/services/dataservice'
import { EntityCustomDomain } from 'dbaas/services/dataservice/interface'
import useStores from 'dbaas/stores/useStores'

import { useDataApiContext } from '../../context'

type CustomDomainItem = { custom_domain_name: string; custom_path: string; default_domain_name: string }
enum ColorMapKey {
  PENDING = 'pending',
  FAILURE = 'failure',
  SUCCESS = 'success',
  EXPIRED = 'expired'
}

const CopyText: React.FC<React.PropsWithChildren<{ text: string }>> = ({ text }) => {
  return (
    <CopyButton value={text} timeout={2000}>
      {({ copied, copy }) => (
        <Tooltip label={copied ? 'Copied' : 'Copy'} withArrow position="top">
          <ActionIcon aria-label="Copy" color="carbon.8" variant="transparent" onClick={copy}>
            <IconCopy01 size={14} />
          </ActionIcon>
        </Tooltip>
      )}
    </CopyButton>
  )
}

const CustomDomain = ({ defaultDomains, defaultPath }: { defaultDomains: string[]; defaultPath: string }) => {
  const [opened, setOpened] = useState(false)
  const [removeOpened, setRemoveOpened] = useState(false)
  const [dnsOpened, setDnsOpened] = useState(false)
  const [dnsPreviewItem, setDnsPreviewItem] = useState<EntityCustomDomain | null>(null)
  const form = useForm<CustomDomainItem>({
    mode: 'onBlur'
  })
  const { reset, watch } = form
  const customPathVal = watch('custom_path')
  const customDomainVal = watch('custom_domain_name')
  const defaultDomainVal = watch('default_domain_name')
  const {
    store: { tenantData, activeProjectId }
  } = useStores()
  const { editApp } = useDataApiContext()
  const [removedItem, setRemovedItem] = useState<EntityCustomDomain | null>(null)

  const createMutaion = useCreateCustomDomain()
  const updateMutaion = useEditCustomDomain()
  const deleteItemMutation = useDeleteCustomDomain()
  const [id, setId] = useState(0)
  const [editCustomDomainItem, setEditCustomDomainItem] = useState<EntityCustomDomain | null>(null)

  const {
    data: list,
    refetch: refetCustomDomains,
    isRefetching
  } = useGetCustomDomains(tenantData.id, activeProjectId, editApp?.id ?? '', {
    query: {
      enabled: !!tenantData.id && !!activeProjectId && !!editApp?.id,
      refetchOnWindowFocus: false,
      onSettled: () => {
        setId(id ? 0 : 1)
      }
    }
  })

  const colorMap = {
    [ColorMapKey.PENDING]: 'carbon.6',
    [ColorMapKey.FAILURE]: 'red.7',
    [ColorMapKey.SUCCESS]: 'green.7',
    [ColorMapKey.EXPIRED]: 'carbon.6'
  }

  const columns = useMemo<MRT_ColumnDef<EntityCustomDomain>[]>(
    () => [
      {
        header: 'Custom Domain',
        accessorFn: (column) => <Typography variant="body-sm">{column.custom_domain_name}</Typography>
      },
      {
        header: 'Default Domain',
        size: 100,
        accessorFn: (column) => <Typography variant="body-sm">{column.default_domain_name}</Typography>
      },
      {
        header: 'Status',
        size: 140,
        accessorFn: (column) => (
          <Flex align="center">
            {column.state && (
              <Badge
                color={colorMap[column.state.toLocaleLowerCase() as ColorMapKey]}
                variant="dot"
                size="lg"
                sx={{ padding: 0 }}
              ></Badge>
            )}
            <Typography variant="body-sm" mr={8}>
              {column.state}
            </Typography>
            <HoverCard width={500} withArrow position="left" shadow="md">
              <HoverCard.Target>
                <Box component={IconInfoCircle} size={14} c="carbon.8" ml={2} sx={{ cursor: 'pointer' }}></Box>
              </HoverCard.Target>
              <HoverCard.Dropdown>
                {column.state !== 'Failure' && column.state !== 'Expired' ? (
                  <Typography variant="body-sm">
                    DNS {column.state === 'Pending' ? 'validation pending' : 'validated successfully'}. Click to view{' '}
                    <Box
                      c="blue"
                      component="a"
                      mr={4}
                      onClick={() => {
                        setDnsOpened(true)
                        setDnsPreviewItem(column)
                      }}
                    >
                      DNS Settings.
                    </Box>
                    {column.state === 'Pending'
                      ? 'Depending on your DNS provider, it might take up to 24 hours for the DNS record to be added.'
                      : ''}
                  </Typography>
                ) : (
                  <Typography>{column.reason}</Typography>
                )}
              </HoverCard.Dropdown>
            </HoverCard>
          </Flex>
        )
      },
      {
        header: 'Action',
        size: 100,
        id: `${id}`,
        accessorFn: (column) => (
          <Flex>
            <ActionIcon
              aria-label="Refresh"
              loading={isRefetching}
              variant="subtle"
              onClick={() => {
                setId(id ? 0 : 1)
                refetCustomDomains()
              }}
            >
              <IconRefreshCw02 size={16} />
            </ActionIcon>

            <ActionIcon
              aria-label="Edit"
              variant="subtle"
              disabled={column.expired}
              onClick={() => {
                setEditCustomDomainItem(column)
                setOpened(true)
              }}
            >
              <IconEdit05 size={16} />
            </ActionIcon>

            <ActionIcon
              aria-label="Remove"
              variant="subtle"
              onClick={() => {
                setRemovedItem(column)
                setRemoveOpened(true)
              }}
            >
              <IconTrash01 size={16} />
            </ActionIcon>
          </Flex>
        )
      }
    ],
    [list?.data, isRefetching]
  )

  const customDomainSubmitHandler = async (values: CustomDomainItem) => {
    if (editCustomDomainItem) {
      await updateMutaion.mutateAsync({
        orgId: tenantData.id,
        projectId: activeProjectId,
        appId: editApp?.id ?? '',
        customDomainId: editCustomDomainItem.id ?? 0,
        data: {
          ...editCustomDomainItem,
          custom_domain_name: values.custom_domain_name,
          custom_path: values.custom_path
        }
      })
    } else {
      await createMutaion.mutateAsync({
        orgId: tenantData.id,
        projectId: activeProjectId,
        appId: editApp?.id ?? '',
        data: values
      })
    }

    refetCustomDomains()
    closeAddModal()

    if (!(editCustomDomainItem && editCustomDomainItem.custom_domain_name === values.custom_domain_name)) {
      // open dns settings: create or change domain
      setDnsPreviewItem({
        custom_domain_name: values.custom_domain_name,
        default_domain_name: values.default_domain_name
      })
      setDnsOpened(true)
    }
  }

  const closeRemoveModal = () => {
    setRemovedItem(null)
    setRemoveOpened(false)
  }

  const closeAddModal = () => {
    setOpened(false)
    reset()
    setEditCustomDomainItem(null)
  }

  const removeCustomHandler = async () => {
    await deleteItemMutation.mutateAsync({
      orgId: tenantData.id,
      projectId: activeProjectId,
      appId: editApp?.id ?? '',
      customDomainId: removedItem?.id ?? 0
    })

    closeRemoveModal()
    refetCustomDomains()
  }

  const previewBaseUrl = useMemo(() => {
    if (!opened) {
      return
    }

    const customDomain = (customDomainVal ?? '').trim()
    const prefix = customDomain.length ? customDomain : (defaultDomainVal ?? '')

    const path = (customPathVal ?? '').trim()
    const affix = path.length ? path : defaultPath
    return `https://${prefix}${affix}`
  }, [customDomainVal, customPathVal, defaultDomainVal, opened])

  const domainList = useMemo(() => {
    return defaultDomains.filter((item) => !list?.data?.some((domain) => domain.default_domain_name === item))
  }, [defaultDomains, list?.data])

  useEffect(() => {
    if (!opened) {
      return
    }

    if (!editCustomDomainItem && !domainList.length) {
      return
    }

    setTimeout(() => {
      form.setValue(
        'default_domain_name',
        editCustomDomainItem ? (editCustomDomainItem.default_domain_name ?? '') : domainList[0]
      )
      if (editCustomDomainItem) {
        form.setValue('custom_domain_name', editCustomDomainItem?.custom_domain_name ?? '')
        form.setValue('custom_path', editCustomDomainItem?.custom_path ?? '')
      }
    })
  }, [opened, domainList])

  return (
    <Card mt={24} p={24}>
      <Flex justify="space-between" align="center">
        <Box sx={{ whiteSpace: 'break-spaces' }}>
          <Typography variant="title-md" mb={4}>
            Manage Custom Domain
          </Typography>
          <Typography variant="body-md" component="span">
            A custom domain allows your website to be served from a domain other than default domain. You will need to
            change settings with your DNS provider. For further usage of custom domain, please refer to our{' '}
            <a href="https://docs.pingcap.com/tidbcloud/data-service-custom-domain" target="_blank" rel="noreferrer">
              documentation
            </a>
            .
          </Typography>
        </Box>
        <Button
          size="compact-xs"
          h={28}
          ml={32}
          variant="default"
          data-mp-event="Add Cluster button clicked"
          data-mp-source="dataapp"
          disabled={!!(list?.data?.length && !domainList.length)}
          onClick={() => setOpened(true)}
        >
          Add Custom Domain
        </Button>
      </Flex>
      {!!list?.data?.length && (
        <Box mt={12}>
          <ProTable
            mantinePaperProps={{ mx: 24 }}
            mantineTableHeadCellProps={{
              sx: {
                ':first-of-type': { paddingLeft: '24px !important' }
              }
            }}
            mantineTableBodyCellProps={{
              sx: {
                ':first-of-type': { paddingLeft: '24px !important' }
              }
            }}
            withBorder
            columns={columns}
            data={list.data}
            emptyMessage="No data"
          />
        </Box>
      )}
      <Modal
        title={`${editCustomDomainItem ? 'Edit' : 'Add'} Custom Domain`}
        opened={opened}
        onClose={closeAddModal}
        closeOnClickOutside={false}
        size={640}
        centered
        trapFocus={false}
      >
        <Typography variant="title-md">Configure Domain</Typography>
        <Typography variant="body-sm" mt={8}>
          Custom Domain Management allows you to add your own unique domain for a personalized experience, and
          optionally adjust the path within the Base URL to precisely define the location of your resources. This
          flexibility empowers you to tailor your endpoint structure according to your specific needs.
        </Typography>

        <Typography variant="body-sm" mb={24}>
          Connect your data app with a domain you own.
        </Typography>

        <Form<CustomDomainItem>
          form={form}
          onSubmit={customDomainSubmitHandler}
          onCancel={closeAddModal}
          formMode="onChange"
          actionsProps={{
            confirmText: 'Save',
            loading: createMutaion.isLoading || updateMutaion.isLoading,
            disabled:
              !(customDomainVal ?? '').trim().length ||
              (!editCustomDomainItem && !!list?.data?.length && !domainList.length)
          }}
        >
          <FormSelect
            data={
              editCustomDomainItem
                ? [
                    {
                      label: editCustomDomainItem.default_domain_name ?? '',
                      value: editCustomDomainItem.default_domain_name ?? ''
                    }
                  ]
                : domainList.map((item) => ({
                    label: item,
                    value: item
                  }))
            }
            name="default_domain_name"
            label="Default Domain"
            disabled={!!editCustomDomainItem}
          ></FormSelect>

          <FormTextInput
            name="custom_domain_name"
            label="Custom Domain"
            defaultValue={editCustomDomainItem?.custom_domain_name}
            placeholder="example: www.test.com"
            rules={{
              required: 'Please input your custom domain.',
              validate: (val: string) => {
                if (val.startsWith('http://')) {
                  return 'Please note that we only support the HTTPS protocol. And there is no need to write the prefix "https://". Example: www.test.com'
                }

                if (val.startsWith('https://')) {
                  return 'Please remove the "https://" prefix. Example: www.test.com'
                }

                if (!/^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/.test(val)) {
                  return 'Illegal domain name format. Example: www.test.com'
                }
              },
              maxLength: {
                value: 255,
                message: 'The input is too long, exceeding 255 characters.'
              }
            }}
          ></FormTextInput>

          <Accordion
            variant="filled"
            chevron={<IconChevronRightFill />}
            styles={() => ({
              control: {
                padding: '4px 0'
              },
              content: {
                padding: 0
              },
              chevron: {
                transform: 'rotate(90deg)',
                '&[data-rotate]': {
                  transform: 'rotate(270deg)'
                }
              }
            })}
          >
            <Accordion.Item value="path">
              <Accordion.Control>
                <Flex align="center">
                  <Typography variant="title-md">Configure Path</Typography>
                  <Typography variant="title-md" c="carbon.7" ml={4}>
                    (Optional)
                  </Typography>
                </Flex>
              </Accordion.Control>
              <Accordion.Panel>
                <Typography variant="body-sm">
                  Configure the path within the Base URL to define the location of your resources. For optimal
                  organization, consider including a version in your path, such as '/v1'. A versioned path ensures
                  future compatibility, allowing seamless updates while maintaining backward compatibility with existing
                  endpoint.
                </Typography>
                <FormTextInput
                  name="default_path"
                  disabled
                  label="Default Path"
                  my={24}
                  value={defaultPath}
                ></FormTextInput>

                <FormTextInput
                  name="custom_path"
                  label="Custom Path"
                  placeholder="example: /v1"
                  defaultValue={editCustomDomainItem?.custom_path}
                  rules={{
                    pattern: {
                      value: /^\/$|^\/[A-Za-z0-9-_]+(\/[A-Za-z0-9-_]+)*$/,
                      message: 'Illegal path name format. Example: /v1'
                    },
                    maxLength: {
                      value: 255,
                      message: 'The input is too long, exceeding 255 characters.'
                    }
                  }}
                ></FormTextInput>
              </Accordion.Panel>
            </Accordion.Item>
          </Accordion>

          <Typography variant="title-md" mt={12}>
            Preview Your Base URL
          </Typography>
          <Input disabled value={previewBaseUrl} />
        </Form>
      </Modal>
      <Modal
        title="Remove Custom Domain"
        opened={removeOpened}
        onClose={closeRemoveModal}
        size={640}
        centered
        trapFocus={false}
      >
        <Typography variant="label-lg" fw={400} mb={8}>
          Removing the custom domain will revert the base URL to its default path
          <Code fw={600} bg="carbon.3" ml={4}>
            https://{removedItem?.default_domain_name ?? ''}
            {defaultPath}
          </Code>
          . As a result, the host with the domain
          <Code fw={600} bg="carbon.3" ml={4}>
            {removedItem?.custom_domain_name ?? ''}
          </Code>{' '}
          will no longer be accessible, effective immediately.
        </Typography>

        <Typography variant="label-lg" fw={400} mt={16}>
          Are you sure you want to proceed with the removal of the custom domain
          <Code fw={600} bg="carbon.3" ml={4}>
            {removedItem?.custom_domain_name ?? ''}
          </Code>
          ?
        </Typography>

        <Flex gap={16} justify="flex-end" mt={24}>
          <Button variant="default" onClick={closeRemoveModal}>
            Cancel
          </Button>
          <Button variant="light" color="red" onClick={removeCustomHandler} loading={deleteItemMutation.isLoading}>
            I understand, remove it
          </Button>
        </Flex>
      </Modal>
      <Modal
        title="DNS Settings"
        opened={dnsOpened}
        onClose={() => {
          setDnsOpened(false)
          setDnsPreviewItem(null)
        }}
        size={640}
        centered
        trapFocus={false}
      >
        <Typography>
          Please add the following CNAME record to your DNS provider. For CNAME configuration, please refer to your
          domain provider's documentation. The process may vary, and your provider's instructions will guide you on how
          to set up the CNAME record effectively.
        </Typography>
        <Form onSubmit={() => {}} withActions={false}>
          <FormTextInput
            mt={24}
            name="name"
            disabled
            label="Name"
            value={dnsPreviewItem?.custom_domain_name ?? ''}
            rightSection={<CopyText text={dnsPreviewItem?.custom_domain_name ?? ''} />}
          ></FormTextInput>
          <FormTextInput
            name="value"
            disabled
            label="Value"
            value={dnsPreviewItem?.default_domain_name ?? ''}
            rightSection={<CopyText text={dnsPreviewItem?.default_domain_name ?? ''} />}
          ></FormTextInput>
        </Form>
      </Modal>
    </Card>
  )
}

export default CustomDomain
