import {
  ActionIcon,
  Alert,
  Badge,
  Box,
  Button,
  Flex,
  Group,
  Menu,
  TextInput,
  Typography,
  closeModal,
  openModal,
  notifier
} from '@tidbcloud/uikit'
import { MRT_ColumnDef, ProTable } from '@tidbcloud/uikit/biz'
import { IconDotsHorizontal } from '@tidbcloud/uikit/icons'
import { observer } from 'mobx-react'
import { useState } from 'react'

import { CopyActionIcon } from 'dbaas/components/Copy/CopyActionIcon'
import { ModalCodeTitle } from 'dbaas/components/Modal'
import { AccessDeniedHoverHint } from 'dbaas/contexts/access-control'
import { addDomain, deleteDomain, verifyDomain } from 'dbaas/services/account'
import { CentralOrgDomain } from 'dbaas/services/account/interface'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import { domainRegEx } from 'dbaas/utils/regex'

export enum DomainStatus {
  VERIFIED = 'verified',
  UNVERIFIED = 'unverified',
  OWNER_CONFIRMED = 'owner_confirmed'
}

interface DomainsProps {
  orgId: string
  domains: CentralOrgDomain[]
  loading?: boolean
  isError?: boolean
  actionAccessible?: boolean
  onRefresh: () => Promise<unknown>
}

const normalizeDomain = (value: string) => value.trim().toLowerCase()

const getDomainName = (domain: CentralOrgDomain) => domain.domain_name || ''

const getVerificationHost = (domain: CentralOrgDomain) => {
  const domainName = getDomainName(domain)
  const host = domain.host || ''

  if (!host || host === '@') {
    return domainName
  }
  if (host === domainName || host.endsWith(`.${domainName}`)) {
    return host
  }
  return `${host}.${domainName}`
}

const getVerificationTxtValue = (token?: string) => {
  const value = token || ''
  return value && !value.startsWith('tidbcloud-verify=') ? `tidbcloud-verify=${value}` : value
}

const getStatusLabel = (status?: string) => {
  switch (status) {
    case DomainStatus.VERIFIED:
      return 'Verified'
    case DomainStatus.OWNER_CONFIRMED:
      return 'Owner confirmed'
    case DomainStatus.UNVERIFIED:
      return 'Unverified'
    default:
      return status || '-'
  }
}

const getStatusColor = (status?: string) => {
  switch (status) {
    case DomainStatus.VERIFIED:
      return 'peacock'
    case DomainStatus.OWNER_CONFIRMED:
      return 'yellow'
    default:
      return 'carbon'
  }
}

const VerifyModalContent = ({
  domain,
  onVerify,
  onCancel,
  showVerifyLater = false
}: {
  domain: CentralOrgDomain
  onVerify: () => Promise<void>
  onCancel: () => void
  showVerifyLater?: boolean
}) => {
  const [loading, setLoading] = useState(false)
  const host = getVerificationHost(domain)
  const verificationTxtValue = getVerificationTxtValue(domain.verification_token)

  const handleVerify = async () => {
    setLoading(true)
    try {
      await onVerify()
    } finally {
      setLoading(false)
    }
  }

  return (
    <Box data-testid="domain-verify-modal" mx={-24} mb={-24}>
      <Box px={24} pb={24}>
        <Typography variant="body-lg" mb={24}>
          1. Log in to your DNS provider
          <br />
          2. Add the following TXT record.
        </Typography>
        <Typography variant="body-lg" mb={16}>
          Please be aware that DNS changes can take 5-60 minutes.
        </Typography>

        <Box
          sx={(theme) => ({
            border: `1px solid ${theme.colors.carbon[3]}`,
            borderRadius: theme.radius.md,
            overflow: 'hidden'
          })}
        >
          <Flex px={16} py={12} bg="carbon.2" gap={24}>
            <Typography variant="label-sm" c="carbon.7" sx={{ flex: '0 0 48px' }}>
              Type
            </Typography>
            <Typography variant="label-sm" c="carbon.7" sx={{ flex: '0 0 180px' }}>
              Host
            </Typography>
            <Typography variant="label-sm" c="carbon.7">
              Value
            </Typography>
          </Flex>
          <Flex px={16} py={16} gap={24} align="center">
            <Typography variant="body-md" sx={{ flex: '0 0 48px' }}>
              TXT
            </Typography>
            <Flex align="center" gap={6} sx={{ flex: '0 0 180px', minWidth: 0 }}>
              <Typography variant="body-md" sx={{ wordBreak: 'break-all' }}>
                {host}
              </Typography>
              <CopyActionIcon
                text={host}
                actionIconProps={{ size: 28, variant: 'subtle' }}
                data-testid="domain-verification-host-copy"
                data-mp-event="Copy Domain Verification Host"
              />
            </Flex>
            <Flex align="center" gap={6} sx={{ flex: 1, minWidth: 0 }}>
              <Typography variant="body-md" sx={{ wordBreak: 'break-all' }}>
                {verificationTxtValue}
              </Typography>
              <CopyActionIcon
                text={verificationTxtValue}
                actionIconProps={{ size: 28, variant: 'subtle' }}
                data-testid="domain-verification-token-copy"
                data-mp-event="Copy Domain Verification Token"
              />
            </Flex>
          </Flex>
        </Box>
      </Box>

      <Flex
        justify="flex-end"
        align="center"
        gap={12}
        px={24}
        py={16}
        sx={(theme) => ({ borderTop: `1px solid ${theme.colors.carbon[3]}` })}
      >
        {showVerifyLater && (
          <Typography variant="body-sm" c="carbon.6" style={{ marginRight: 'auto' }}>
            Step 2 of 2
          </Typography>
        )}
        <Button variant="outline" onClick={onCancel} color="carbon" data-mp-event="Cancel Verify Domain">
          {showVerifyLater ? 'Verify later' : 'Cancel'}
        </Button>
        <Button onClick={handleVerify} loading={loading} color="carbon" data-mp-event="Submit Verify Domain">
          Verify
        </Button>
      </Flex>
    </Box>
  )
}

const DeleteDomainConfirm = ({ domain, onDelete }: { domain: CentralOrgDomain; onDelete: () => Promise<void> }) => {
  const [loading, setLoading] = useState(false)
  const domainName = getDomainName(domain)

  const handleDelete = async () => {
    setLoading(true)
    try {
      await onDelete()
    } finally {
      setLoading(false)
    }
  }

  return (
    <Box data-testid="domain-delete-modal">
      <Typography variant="body-lg" mb={32}>
        Are you sure you want to delete the domain <ModalCodeTitle>{domainName}</ModalCodeTitle>?
      </Typography>
      <Flex justify="flex-end" gap={12}>
        <Button
          variant="outline"
          onClick={() => closeModal('delete-domain-modal')}
          color="carbon"
          data-mp-event="Cancel Delete Domain"
        >
          Cancel
        </Button>
        <Button color="red" onClick={handleDelete} loading={loading} data-mp-event="Submit Delete Domain">
          Delete
        </Button>
      </Flex>
    </Box>
  )
}

const AddDomainModal = ({
  orgId,
  onAdded,
  openVerifyModal
}: {
  orgId: string
  onAdded: () => Promise<unknown>
  openVerifyModal: (domain: CentralOrgDomain, showVerifyLater?: boolean) => void
}) => {
  const [domainName, setDomainName] = useState('')
  const [error, setError] = useState<string | null>(null)
  const [loading, setLoading] = useState(false)
  const addModalId = 'add-domain-modal'

  const onNext = async () => {
    const normalizedDomain = normalizeDomain(domainName)
    if (!normalizedDomain) {
      setError('Please enter a domain name.')
      return
    }
    if (!domainRegEx.test(normalizedDomain)) {
      setError('Invalid domain name')
      return
    }

    setError(null)
    setLoading(true)
    try {
      const createdDomain = await addDomain(orgId, { domain_name: normalizedDomain })
      closeModal(addModalId)
      openVerifyModal(createdDomain, true)
      onAdded().catch(() => {
        notifier.error('Domain added, but the domain list failed to refresh.')
      })
    } catch (e) {
      notifier.error(getErrorMessage(e))
    } finally {
      setLoading(false)
    }
  }

  return (
    <Box data-testid="domain-add-modal" mx={-24} mb={-24}>
      <Box px={24} pb={24}>
        <TextInput
          label="Domain name"
          placeholder="example.com"
          value={domainName}
          error={error}
          onChange={(e) => setDomainName(e.target.value)}
          data-testid="domain-add-input"
        />
      </Box>
      <Flex
        justify="flex-end"
        align="center"
        gap={12}
        px={24}
        py={16}
        sx={(theme) => ({ borderTop: `1px solid ${theme.colors.carbon[3]}` })}
      >
        <Typography variant="body-sm" c="carbon.6" style={{ marginRight: 'auto' }}>
          Step 1 of 2
        </Typography>
        <Button
          variant="outline"
          onClick={() => closeModal(addModalId)}
          color="carbon"
          data-mp-event="Cancel Add Domain"
        >
          Cancel
        </Button>
        <Button onClick={onNext} color="carbon" loading={loading} data-mp-event="Submit Add Domain">
          Add domain and next
        </Button>
      </Flex>
    </Box>
  )
}

export const Domains = observer(
  ({ orgId, domains, loading, isError, actionAccessible = true, onRefresh }: DomainsProps) => {
    const canOperate = actionAccessible

    const openVerifyModal = (domain: CentralOrgDomain, showVerifyLater = false) => {
      if (!canOperate) {
        return
      }

      const domainName = getDomainName(domain)
      const domainId = domain.id
      const modalId = showVerifyLater ? 'verify-domain-modal-step2' : 'verify-domain-modal'

      const onVerify = async () => {
        if (!domainId) {
          notifier.error('Domain ID is missing.')
          return
        }
        try {
          await verifyDomain(orgId, domainId)
          notifier.success(`"${domainName}" verified`)
          closeModal(modalId)
          onRefresh().catch(() => {
            notifier.error('Domain verified, but the domain list failed to refresh.')
          })
        } catch (e) {
          notifier.error(getErrorMessage(e))
        }
      }

      openModal({
        modalId,
        title: (
          <Group gap={8}>
            <Typography variant="title-lg">Verify</Typography>
            <ModalCodeTitle>{domainName}</ModalCodeTitle>
          </Group>
        ),
        size: 640,
        centered: true,
        children: (
          <VerifyModalContent
            domain={domain}
            onVerify={onVerify}
            onCancel={() => closeModal(modalId)}
            showVerifyLater={showVerifyLater}
          />
        )
      })
    }

    const handleDelete = (domain: CentralOrgDomain) => {
      if (!canOperate) {
        return
      }

      const domainName = getDomainName(domain)
      const domainId = domain.id

      openModal({
        modalId: 'delete-domain-modal',
        title: (
          <Group gap={8}>
            <Typography variant="title-lg">Delete Domain</Typography>
            <ModalCodeTitle>{domainName}</ModalCodeTitle>
          </Group>
        ),
        size: 640,
        centered: true,
        children: (
          <DeleteDomainConfirm
            domain={domain}
            onDelete={async () => {
              if (!domainId) {
                notifier.error('Domain ID is missing.')
                return
              }
              try {
                await deleteDomain(orgId, domainId)
                notifier.success(`Domain ${domainName} deleted.`)
                closeModal('delete-domain-modal')
                onRefresh().catch(() => {
                  notifier.error('Domain deleted, but the domain list failed to refresh.')
                })
              } catch (e) {
                notifier.error(getErrorMessage(e))
              }
            }}
          />
        )
      })
    }

    const handleAddDomain = () => {
      if (!canOperate) {
        return
      }

      openModal({
        modalId: 'add-domain-modal',
        title: <Typography variant="title-lg">Add Domain</Typography>,
        size: 640,
        centered: true,
        children: <AddDomainModal orgId={orgId} onAdded={onRefresh} openVerifyModal={openVerifyModal} />
      })
    }

    const columns: MRT_ColumnDef<CentralOrgDomain>[] = [
      {
        header: 'Domains',
        accessorKey: 'domain_name',
        mantineTableHeadCellProps: {
          sx: { width: '50% !important' }
        }
      },
      {
        header: 'Status',
        accessorFn: (row) => (
          <Badge variant="dot" color={getStatusColor(row.status)}>
            {getStatusLabel(row.status)}
          </Badge>
        ),
        mantineTableHeadCellProps: {
          sx: { width: '40% !important' }
        }
      },
      {
        header: '',
        id: 'action',
        mantineTableHeadCellProps: {
          sx: {
            width: '60px !important'
          }
        },
        mantineTableBodyCellProps: {
          align: 'right'
        },
        Cell: ({ row }: { row: { original: CentralOrgDomain } }) => (
          <Menu position="bottom-end" shadow="md" width={120} withinPortal>
            <Menu.Target>
              <ActionIcon
                variant="subtle"
                color="carbon"
                disabled={!canOperate}
                onClick={(e) => e.stopPropagation()}
                data-mp-event="Domain Actions Clicked"
              >
                <IconDotsHorizontal size={16} />
              </ActionIcon>
            </Menu.Target>
            <Menu.Dropdown>
              {row.original.status !== DomainStatus.VERIFIED && (
                <Menu.Item
                  onClick={(event) => {
                    event.stopPropagation()
                    openVerifyModal(row.original)
                  }}
                  data-mp-event="Verify Domain Clicked"
                >
                  Verify
                </Menu.Item>
              )}
              <Menu.Item
                c="red.7"
                onClick={(event) => {
                  event.stopPropagation()
                  handleDelete(row.original)
                }}
                data-mp-event="Delete Domain Clicked"
              >
                Delete
              </Menu.Item>
            </Menu.Dropdown>
          </Menu>
        )
      }
    ]

    return (
      <Box mt={24} data-testid="domains-section">
        <Group justify="space-between" mb={16}>
          <Box>
            <Typography variant="title-lg" fz={16}>
              Domains
            </Typography>
            <Typography variant="body-lg" c="carbon.6">
              Verify your organization's domains to securely enable SSO and SCIM provisioning.
            </Typography>
          </Box>
          <AccessDeniedHoverHint id="OrgAuthenticationAction">
            {({ accessible }) => (
              <Button
                variant="outline"
                onClick={handleAddDomain}
                color="carbon"
                size="sm"
                disabled={!accessible || !canOperate}
                data-mp-event="Add Domain Button Clicked"
                data-testid="domains-add-button"
              >
                Add Domain
              </Button>
            )}
          </AccessDeniedHoverHint>
        </Group>
        {isError && (
          <Alert color="red" mb={16}>
            Domains failed to load. Refresh the page or try again later.
          </Alert>
        )}
        <ProTable columns={columns} data={domains} loading={loading} emptyMessage="No domains" />
      </Box>
    )
  }
)
