import {
  Alert,
  Anchor,
  Badge,
  Box,
  Button,
  Card,
  Checkbox,
  Divider,
  Group,
  Loader,
  Modal,
  Select,
  Stack,
  Typography,
  notifier
} from '@tidbcloud/uikit'
import { DateTimePicker, DotBadge, useDateTimePicker } from '@tidbcloud/uikit/biz'
import { useDisclosure } from '@tidbcloud/uikit/hooks'
import { IconInfoCircle } from '@tidbcloud/uikit/icons'
import { useEffect, useMemo, useState } from 'react'

import { ReasonHoverHint, type HoverHintReason } from 'dbaas/components/HoverHint/ReasonHoverHint'
import { HeroComponent, isHeroQueryReady } from 'dbaas/components/Measurement'
import { useAccessDeniedHintReason } from 'dbaas/contexts/access-control'
import { getTidbStateToPassivePhrase } from 'dbaas/screens/NextGen/shared/model/tidb-state'
import {
  usePlanReplayerServiceGetPlanReplayerStatus,
  usePlanReplayerServiceUpdatePlanReplayerStatus
} from 'dbaas/services/api-clients/v2025/tidb-management/v1beta2'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import useStores from 'dbaas/stores/useStores'
import { formatTime } from 'dbaas/utils/format'
import { MixpanelEvents } from 'dbaas/utils/tracking'

import { SharedTiDB } from '../../shared/model/shared-tidb-state'

import {
  ACCESS_DURATION_OPTIONS,
  getExpireTimeForDuration,
  isPlanReplayerAccessActive
} from './plan-replayer-access-utils'

const SQL_PLAN_REPLAYER_DOCS_URL = {
  essential: 'https://docs.pingcap.com/tidbcloud/plan-replayer/?plan=essential',
  premium: 'https://docs.pingcap.com/tidbcloud/plan-replayer/?plan=premium'
} as const
const MAX_TIMEOUT_MS = 2_147_483_647
const DEFAULT_ACCESS_DURATION_DAYS = '7'

const formatUtcOffset = (offset: number) => {
  const absoluteOffset = Math.abs(offset)
  const hours = Math.floor(absoluteOffset)
  const minutes = Math.round((absoluteOffset - hours) * 60)
  return `UTC${offset >= 0 ? '+' : '-'}${hours}:${String(minutes).padStart(2, '0')}`
}

export const formatPlanReplayerTime = (time: string | Date, offset: number) =>
  `${formatTime(time, { offset, showSeconds: true })} ${formatUtcOffset(offset)}`

const formatPlanReplayerAlertTime = (time: string | Date, offset: number) => {
  const utcOffset = formatUtcOffset(offset)
  const compactUtcOffset = utcOffset.endsWith(':00') ? utcOffset.slice(0, -3) : utcOffset
  return `${formatTime(time, { offset, showSeconds: true })} (${compactUtcOffset})`
}

const getPlanReplayerDocsUrl = (isEssential: boolean) =>
  isEssential ? SQL_PLAN_REPLAYER_DOCS_URL.essential : SQL_PLAN_REPLAYER_DOCS_URL.premium

const PlanReplayerDescription = ({ authorized, isEssential }: { authorized: boolean; isEssential: boolean }) => (
  <Typography variant="body-lg" c="carbon.8" data-testid="plan-replayer-access-description">
    {authorized ? (
      <>
        Please contact{' '}
        <Anchor href="https://tidb.support.pingcap.com/servicedesk/customer/portals" target="_blank" rel="noreferrer">
          TiDB Cloud Support Team
        </Anchor>{' '}
        to access to your generated SQL Plan Replayer diagnostic files and assist with SQL performance troubleshooting.
      </>
    ) : (
      <>
        Grants TiDB Cloud support engineers temporary access to your generated SQL Plan Replayer diagnostic files to
        assist with SQL performance troubleshooting.
      </>
    )}{' '}
    These files contain SQL texts, table schemas (DDL), and optimizer statistics, but never contain any actual table row
    data. Learn more about{' '}
    <Anchor href={getPlanReplayerDocsUrl(isEssential)} target="_blank" rel="noreferrer">
      SQLPlan Replayer
    </Anchor>
    .
  </Typography>
)

interface AuthorizeModalProps {
  error?: string
  loading: boolean
  opened: boolean
  onAuthorize: (durationDays: string) => void
  onClose: () => void
}

const AuthorizeModal = ({ error, loading, opened, onAuthorize, onClose }: AuthorizeModalProps) => {
  const [duration, setDuration] = useState(DEFAULT_ACCESS_DURATION_DAYS)
  const [confirmed, setConfirmed] = useState(false)

  useEffect(() => {
    if (opened) {
      setDuration(DEFAULT_ACCESS_DURATION_DAYS)
      setConfirmed(false)
    }
  }, [opened])

  return (
    <Modal
      size={640}
      title="Authorize SQL Plan Replayer File Access"
      opened={opened}
      onClose={onClose}
      data-testid="plan-replayer-authorize-modal"
    >
      <Stack gap="md">
        <Typography variant="body-lg" c="carbon.7">
          To help our Support Engineers troubleshoot your SQL performance issues, please authorize temporary access to
          SQL Plan Replayer files. You can revoke it at any time.
        </Typography>
        <Box>
          <Select
            label="Access duration"
            data={ACCESS_DURATION_OPTIONS}
            value={duration}
            onChange={(value) => value && setDuration(value)}
            allowDeselect={false}
            withCheckIcon={false}
            renderOption={({ option }) => (
              <Group gap="sm">
                <span>{option.label}</span>
                {option.value === '7' && (
                  <Badge color="gray" size="xs" variant="outline" radius="xl">
                    RECOMMENDED
                  </Badge>
                )}
              </Group>
            )}
            data-testid="plan-replayer-access-duration-select"
          />
          <Typography variant="body-sm" c="carbon.7" mt={6}>
            Access is temporary. It starts immediately and is automatically revoked when the selected duration ends.
          </Typography>
        </Box>
        <Checkbox
          checked={confirmed}
          onChange={(event) => setConfirmed(event.currentTarget.checked)}
          label={
            <Typography variant="label-lg">
              I authorize TiDB Cloud Support to access SQL Plan Replayer files during this period.
            </Typography>
          }
          data-testid="plan-replayer-authorize-confirmation"
        />
        {error && (
          <Alert color="red" data-testid="plan-replayer-authorize-error">
            {error}
          </Alert>
        )}
      </Stack>
      <Divider mx="-md" mt="lg" />
      <Group justify="flex-end" mt="md">
        <Button
          variant="default"
          onClick={onClose}
          data-testid="plan-replayer-authorize-cancel"
          data-mp-event={MixpanelEvents.PremiumPlanReplayer.Enable.CancelClicked}
        >
          Cancel
        </Button>
        <Button
          disabled={!confirmed}
          loading={loading}
          onClick={() => onAuthorize(duration)}
          data-testid="plan-replayer-authorize-submit"
          data-mp-event={MixpanelEvents.PremiumPlanReplayer.Enable.SubmitClicked}
          data-mp-access-duration-days={duration}
        >
          Authorize
        </Button>
      </Group>
    </Modal>
  )
}

interface UpdateExpirationModalProps {
  currentExpiration: Date
  error?: string
  loading: boolean
  opened: boolean
  organizationTimezone: number
  onClose: () => void
  onUpdate: (expiration: Date) => void
}

const UpdateExpirationModal = ({
  currentExpiration,
  error,
  loading,
  opened,
  organizationTimezone,
  onClose,
  onUpdate
}: UpdateExpirationModalProps) => {
  const [newExpiration, setNewExpiration] = useState(currentExpiration)
  const [openedAt, setOpenedAt] = useState(new Date())
  const localTimezone = -(new Date().getTimezoneOffset() / 60)

  useEffect(() => {
    if (opened) {
      setNewExpiration(currentExpiration)
      setOpenedAt(new Date())
    }
  }, [currentExpiration, opened])

  const dateTimePickerProps = useDateTimePicker({
    value: newExpiration,
    onChange: setNewExpiration,
    today: openedAt,
    highlightToday: true,
    startDate: new Date(currentExpiration.getTime() + 1000),
    utcOffset: organizationTimezone,
    formatter: (date) => formatPlanReplayerTime(date, organizationTimezone)
  })

  const canUpdate = newExpiration.getTime() > currentExpiration.getTime()

  return (
    <Modal
      size={520}
      title="Update Access"
      opened={opened}
      onClose={onClose}
      data-testid="plan-replayer-update-expiration-modal"
    >
      <Stack gap="md">
        <Typography variant="body-lg" c="carbon.7">
          To help our Support Engineers to better troubleshoot your SQL performance issues, you can postpone the
          expiration date.
        </Typography>
        <Box>
          <Typography variant="label-lg">Current Expiration Date</Typography>
          <Typography variant="headline-sm" c="carbon.8" mt={6}>
            {formatPlanReplayerTime(currentExpiration, organizationTimezone)}
          </Typography>
        </Box>
        <Box data-testid="plan-replayer-new-expiration-picker">
          <Typography variant="label-lg" mb={6}>
            New Expiration Date
          </Typography>
          <DateTimePicker
            {...dateTimePickerProps}
            footer={
              <Stack gap={2} align="center" py="xs" data-testid="plan-replayer-timezone-hint">
                <Typography variant="body-sm" c="carbon.7">
                  The system display is based on the time zone set by the organization
                </Typography>
                <Typography variant="body-sm" c="carbon.7">
                  Organization Time: {formatPlanReplayerTime(openedAt, organizationTimezone)}
                </Typography>
                <Typography variant="body-sm" c="carbon.7">
                  Your Local Time: {formatPlanReplayerTime(openedAt, localTimezone)}
                </Typography>
              </Stack>
            }
          />
        </Box>
        {error && (
          <Alert color="red" data-testid="plan-replayer-update-expiration-error">
            {error}
          </Alert>
        )}
      </Stack>
      <Divider mx="-md" mt="lg" />
      <Group justify="flex-end" mt="md">
        <Button
          variant="default"
          onClick={onClose}
          data-testid="plan-replayer-update-expiration-cancel"
          data-mp-event={MixpanelEvents.PremiumPlanReplayer.Update.CancelClicked}
        >
          Cancel
        </Button>
        <Button
          disabled={!canUpdate}
          loading={loading}
          onClick={() => onUpdate(newExpiration)}
          data-testid="plan-replayer-update-expiration-submit"
          data-mp-event={MixpanelEvents.PremiumPlanReplayer.Update.SubmitClicked}
        >
          Update
        </Button>
      </Group>
    </Modal>
  )
}

interface RevokeModalProps {
  error?: string
  loading: boolean
  opened: boolean
  onClose: () => void
  onRevoke: () => void
}

const RevokeModal = ({ error, loading, opened, onClose, onRevoke }: RevokeModalProps) => (
  <Modal size={480} title="Revoke Access" opened={opened} onClose={onClose} data-testid="plan-replayer-revoke-modal">
    <Stack gap="md">
      <Typography variant="body-lg" c="carbon.8">
        Once revoked, TiDB Cloud Support Engineers will no longer have access to these files. Please ensure your issue
        is resolved before proceeding.
      </Typography>
      <Typography variant="body-lg" c="carbon.8">
        Are you sure you want to revoke access?
      </Typography>
    </Stack>
    {error && (
      <Alert color="red" mt="md" data-testid="plan-replayer-revoke-error">
        {error}
      </Alert>
    )}
    <Group justify="flex-end" mt="xl">
      <Button
        variant="default"
        onClick={onClose}
        data-testid="plan-replayer-revoke-cancel"
        data-mp-event={MixpanelEvents.PremiumPlanReplayer.Disable.CancelClicked}
      >
        Cancel
      </Button>
      <Button
        color="red"
        variant="default"
        loading={loading}
        onClick={onRevoke}
        data-testid="plan-replayer-revoke-submit"
        data-mp-event={MixpanelEvents.PremiumPlanReplayer.Disable.SubmitClicked}
      >
        Revoke
      </Button>
    </Group>
  </Modal>
)

export const PlanReplayerAccess = ({ tidb }: { tidb: SharedTiDB }) => {
  const [authorizeOpened, authorizeModal] = useDisclosure(false)
  const [updateOpened, updateModal] = useDisclosure(false)
  const [revokeOpened, revokeModal] = useDisclosure(false)
  const [mutationError, setMutationError] = useState<string>()

  const {
    store: {
      lastVisitLocation: { orgId, projectId },
      tenantData
    }
  } = useStores()

  const tidbId = tidb.tidbId || ''
  const tidbReady = tidb.computed.ready
  const planReplayerQuery = usePlanReplayerServiceGetPlanReplayerStatus(
    tidbId,
    { orgId, projectId },
    {
      query: {
        enabled: Boolean(tidbId && orgId && projectId && tidbReady),
        retry: 3
      }
    }
  )
  const updateMutation = usePlanReplayerServiceUpdatePlanReplayerStatus()
  const currentExpiration = useMemo(() => {
    const expiration = planReplayerQuery.data?.expireTime
    if (!expiration || !Number.isFinite(Date.parse(expiration))) return undefined
    return new Date(expiration)
  }, [planReplayerQuery.data?.expireTime])
  const authorized = isPlanReplayerAccessActive(planReplayerQuery.data?.enabled, planReplayerQuery.data?.expireTime)
  const refetchPlanReplayerStatus = planReplayerQuery.refetch

  useEffect(() => {
    if (!authorized || !currentExpiration) return

    let timeout: number
    const scheduleExpirationRefresh = () => {
      const delay = currentExpiration.getTime() - Date.now()
      timeout = window.setTimeout(
        () => (delay > MAX_TIMEOUT_MS ? scheduleExpirationRefresh() : refetchPlanReplayerStatus()),
        Math.min(delay + 250, MAX_TIMEOUT_MS)
      )
    }

    scheduleExpirationRefresh()
    return () => window.clearTimeout(timeout)
  }, [authorized, currentExpiration, refetchPlanReplayerStatus])

  const passivePhrase = getTidbStateToPassivePhrase(tidb.state)
  const { reason: accessDeniedReason } = useAccessDeniedHintReason({ id: 'TidbxPlanReplayerAction' })
  const notReadyReason = useMemo<HoverHintReason>(
    () => ({
      id: 'notReady',
      when: !tidbReady,
      content: `This feature is unavailable while the instance is ${passivePhrase}.`
    }),
    [passivePhrase, tidbReady]
  )

  const closeModal = (close: () => void) => {
    setMutationError(undefined)
    close()
  }

  const updateStatus = async (data: { enabled: boolean; expireTime?: string }) => {
    setMutationError(undefined)
    try {
      await updateMutation.mutateAsync({
        tidbId,
        data,
        params: { orgId, projectId }
      })
      await refetchPlanReplayerStatus()
      return true
    } catch (error) {
      setMutationError(getErrorMessage(error))
      return false
    }
  }

  const handleAuthorize = async (durationDays: string) => {
    if (await updateStatus({ enabled: true, expireTime: getExpireTimeForDuration(durationDays) })) {
      authorizeModal.close()
      notifier.success('SQL Plan Replayer file access has been authorized.')
    }
  }

  const handleUpdateExpiration = async (expiration: Date) => {
    if (await updateStatus({ enabled: true, expireTime: expiration.toISOString() })) {
      updateModal.close()
      notifier.success('The access expiration has been updated.')
    }
  }

  const handleRevoke = async () => {
    if (await updateStatus({ enabled: false })) {
      revokeModal.close()
      notifier.success('SQL Plan Replayer file access has been revoked.')
    }
  }

  const openModal = (open: () => void) => {
    setMutationError(undefined)
    open()
  }

  return (
    <Card mt={24} data-testid="plan-replayer-access-card">
      <HeroComponent
        ready={
          !tidbReady || isHeroQueryReady({ isFetched: planReplayerQuery.isFetched, isError: planReplayerQuery.isError })
        }
      />
      <Group justify="space-between" align="flex-start" wrap="nowrap" gap="xl">
        <Stack gap="xs" style={{ flex: 1 }}>
          <Group gap="xs">
            <Typography variant="title-lg">SQL Plan Replayer Files Access Authorization</Typography>
            {authorized && <DotBadge color="green">Authorize Temporary Access</DotBadge>}
          </Group>
          <PlanReplayerDescription
            authorized={authorized}
            isEssential={tidb.computed.isEssential || tidb.computed.isEssentialV2}
          />
        </Stack>
        {planReplayerQuery.isLoading ? (
          <Loader size="sm" />
        ) : authorized ? (
          <Group gap="xs" wrap="nowrap">
            <ReasonHoverHint reasons={[accessDeniedReason, notReadyReason]}>
              {({ matched }) => (
                <Button
                  size="sm"
                  variant="default"
                  disabled={matched}
                  onClick={() => openModal(updateModal.open)}
                  data-testid="plan-replayer-update-expiration-button"
                  data-mp-event={MixpanelEvents.PremiumPlanReplayer.Update.EnterClicked}
                >
                  Update Access
                </Button>
              )}
            </ReasonHoverHint>
            <ReasonHoverHint reasons={[accessDeniedReason, notReadyReason]}>
              {({ matched }) => (
                <Button
                  size="sm"
                  variant="default"
                  color="red"
                  disabled={matched}
                  onClick={() => openModal(revokeModal.open)}
                  data-testid="plan-replayer-revoke-button"
                  data-mp-event={MixpanelEvents.PremiumPlanReplayer.Disable.EnterClicked}
                >
                  Revoke Access
                </Button>
              )}
            </ReasonHoverHint>
          </Group>
        ) : (
          <ReasonHoverHint reasons={[accessDeniedReason, notReadyReason]}>
            {({ matched }) => (
              <Button
                size="sm"
                variant="default"
                disabled={matched}
                onClick={() => openModal(authorizeModal.open)}
                data-testid="plan-replayer-authorize-button"
                data-mp-event={MixpanelEvents.PremiumPlanReplayer.Enable.EnterClicked}
              >
                Authorize
              </Button>
            )}
          </ReasonHoverHint>
        )}
      </Group>
      {authorized && currentExpiration && (
        <Alert color="peacock" icon={<IconInfoCircle size={14} />} mt="md" data-testid="plan-replayer-expiration-alert">
          Access will automatically expire on{' '}
          <strong>{formatPlanReplayerAlertTime(currentExpiration, tenantData.timezone)}</strong>. You can update the
          expiration time or revoke access at any time.
        </Alert>
      )}

      <AuthorizeModal
        opened={authorizeOpened}
        loading={updateMutation.isLoading}
        error={mutationError}
        onClose={() => closeModal(authorizeModal.close)}
        onAuthorize={handleAuthorize}
      />
      {currentExpiration && (
        <UpdateExpirationModal
          opened={updateOpened}
          currentExpiration={currentExpiration}
          organizationTimezone={tenantData.timezone}
          loading={updateMutation.isLoading}
          error={mutationError}
          onClose={() => closeModal(updateModal.close)}
          onUpdate={handleUpdateExpiration}
        />
      )}
      <RevokeModal
        opened={revokeOpened}
        loading={updateMutation.isLoading}
        error={mutationError}
        onClose={() => closeModal(revokeModal.close)}
        onRevoke={handleRevoke}
      />
    </Card>
  )
}
