import { Button, Card, Group, Loader, Modal, notifier, Stack, Typography } from '@tidbcloud/uikit'
import { PropertyCard } from '@tidbcloud/uikit/biz'
import { TransformNullValue } from 'metrics-chart'
import { observer } from 'mobx-react'
import { ReactNode, useMemo, useState } from 'react'
import { useHistory } from 'react-router-dom'

import { MetricInfo } from 'dbaas/components/ChartCard/helpers'
import TimeComponent from 'dbaas/components/TimeComponent'
import { AccessDeniedHoverHint } from 'dbaas/contexts/access-control'
import { ChangefeedNames } from 'dbaas/features/Changefeed'
import { ChangefeedStatus } from 'dbaas/features/Changefeed/ChangefeedStatus'
import { SharedCluster } from 'dbaas/features/ClusterManagement/cluster'
import { ClusterPageShell } from 'dbaas/screens/ClusterV2/components'
import { isClusterInvalid } from 'dbaas/screens/ClusterV2/components/PausedStatus'
import {
  useChangefeedServiceDeleteChangefeed,
  useChangefeedServiceGetChangefeed,
  useChangefeedServiceListChangefeedSpecifications,
  useChangefeedServiceStartChangefeed,
  useChangefeedServiceStopChangefeed
} from 'dbaas/services/api-clients/legacy/serverless'
import {
  Changefeed,
  ChangefeedStateEnum,
  ChangefeedTypeEnum
} from 'dbaas/services/api-clients/legacy/serverless/interface'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import { useGetMetricsQueryRangeURLForCluster } from 'dbaas/services/useO11YQuery'
import useStores from 'dbaas/stores/useStores'

import { SpecificationProperty } from '../ChangefeedDetail/SpecificationProperty'
import { MetricsChartOnChangeFeed } from '../ConnectorCard/MetricsChart'

import { KafkaPropertiesEssential } from './KafkaPropertiesEssential'
import { MySQLPropertiesEssential } from './MySQLPropertiesEssential'

const getMetricsConfig = (changefeed: string): MetricInfo[] => [
  {
    title: 'Latency',
    queries: [
      {
        promql: `max(ticdc_owner_checkpoint_ts_lag{changefeed="${changefeed}"}) by (changefeed)`,
        name: 'changefeed',
        type: 'line'
      }
    ],
    nullValue: TransformNullValue.AS_ZERO,
    unit: 's'
  },
  {
    title: 'Replica rows Per Sec',
    queries: [
      {
        promql: `avg(rate(ticdc_sink_batch_row_count_sum{changefeed="${changefeed}"}[1m])) by (changefeed)`,
        name: 'changefeed',
        type: 'line'
      }
    ],
    nullValue: TransformNullValue.AS_ZERO,
    unit: 'short'
  }
]

const EssentialSpecificationProperty = ({
  orgId,
  projectId,
  clusterId,
  rcu
}: {
  orgId: string
  projectId: string
  clusterId: string
  rcu?: number
}) => {
  const { data, isLoading } = useChangefeedServiceListChangefeedSpecifications(orgId, projectId, clusterId, {
    query: {
      enabled: Boolean(orgId && projectId && clusterId && rcu != null),
      refetchOnWindowFocus: false,
      refetchOnMount: false
    }
  })

  const spec = data?.items?.find((item) => Number(item.rcu) === Number(rcu))
  const rps = spec?.rps_limit ?? 0
  const displayName = isLoading ? 'Loading...' : `${rcu ?? '-'} RCUs`
  const description = `The maximum incremental replication performance is ${rps > 0 ? rps : '-'} rows/s`

  return <SpecificationProperty displayName={displayName} description={description} />
}

const ChangefeedDetailActions = ({
  changefeed,
  refetch,
  cluster
}: {
  changefeed: Changefeed
  refetch: () => void
  cluster: SharedCluster
}) => {
  const history = useHistory()
  const {
    store: { tenantData, activeProjectId }
  } = useStores()

  const [confirmModal, setConfirmModal] = useState<{
    open: boolean
    title?: ReactNode
    children?: ReactNode
    onConfirm?: () => void
  }>({
    open: false
  })

  const deleteChangefeed = useChangefeedServiceDeleteChangefeed()
  const pauseChangefeed = useChangefeedServiceStopChangefeed()
  const resumeChangefeed = useChangefeedServiceStartChangefeed()

  const isPaused = changefeed.state === ChangefeedStateEnum.PAUSED
  const isRunning = changefeed.state === ChangefeedStateEnum.RUNNING
  const isWarning = changefeed.state === ChangefeedStateEnum.WARNING
  const isClusterInvalidState = isClusterInvalid(cluster)
  const canEdit = isPaused

  const handlePauseChangefeed = async () => {
    try {
      await pauseChangefeed.mutateAsync({
        orgId: tenantData.id,
        projectId: activeProjectId,
        clusterId: cluster.id!,
        changefeedId: changefeed.changefeed_id!,
        data: {}
      })
      notifier.success('Changefeed paused successfully')
      setConfirmModal({ open: false })
      refetch()
    } catch (error) {
      notifier.error(getErrorMessage(error))
    }
  }

  const handleResumeChangefeed = async () => {
    try {
      notifier.success('Changefeed is resuming...')
      await resumeChangefeed.mutateAsync({
        orgId: tenantData.id,
        projectId: activeProjectId,
        clusterId: cluster.id!,
        changefeedId: changefeed.changefeed_id!,
        data: {}
      })
      setConfirmModal({ open: false })
      notifier.success('Changefeed resumed successfully')
      refetch()
    } catch (error) {
      notifier.error(getErrorMessage(error))
    }
  }

  const handleDeleteChangefeed = async () => {
    try {
      await deleteChangefeed.mutateAsync({
        orgId: tenantData.id,
        projectId: activeProjectId,
        clusterId: cluster.id!,
        changefeedId: changefeed.changefeed_id!
      })
      notifier.success('Changefeed deleted successfully')
      setConfirmModal({ open: false })
      history.push(`/clusters/${cluster.id}/changefeed`)
    } catch (error) {
      notifier.error(getErrorMessage(error))
    }
  }

  const handlePause = () => {
    setConfirmModal({
      open: true,
      title: `Confirm to pause ${changefeed.display_name || `changefeed ${changefeed.changefeed_id}`}?`,
      children: (
        <Typography variant="body-lg">
          Please resume the changefeed <b>within 24 hours</b>. If a changefeed is paused for longer, TiDB may garbage
          collect data changes, causing the synchronization to fail. A failed changefeed must be deleted and recreated.
        </Typography>
      ),
      onConfirm: handlePauseChangefeed
    })
  }

  const handleDelete = () => {
    setConfirmModal({
      open: true,
      title: `Confirm to delete ${changefeed.display_name || `changefeed ${changefeed.changefeed_id}`}?`,
      children: (
        <Typography variant="body-lg">
          Once a changefeed is deleted, it cannot be recovered, confirm to delete?
        </Typography>
      ),
      onConfirm: handleDeleteChangefeed
    })
  }

  return (
    <>
      <AccessDeniedHoverHint id="TidbxCDCAction" scopeProps={{ tidbId: cluster.id }}>
        {({ accessible }) => (
          <Group gap={8}>
            {(isRunning || isWarning) && (
              <Button
                variant="default"
                size="sm"
                onClick={handlePause}
                disabled={isClusterInvalidState || !accessible}
                loading={pauseChangefeed.isLoading}
                data-mp-event="Pause Changefeed Clicked"
              >
                Pause
              </Button>
            )}
            {isPaused && (
              <Button
                variant="default"
                size="sm"
                onClick={handleResumeChangefeed}
                disabled={isClusterInvalidState || !accessible}
                loading={resumeChangefeed.isLoading}
                data-mp-event="Resume Changefeed Clicked"
              >
                Resume
              </Button>
            )}
            <Button
              variant="default"
              size="sm"
              onClick={() =>
                history.push(`/clusters/${cluster.id}/changefeed/edit-changefeed?id=${changefeed.changefeed_id}`)
              }
              disabled={!canEdit || isClusterInvalidState || !accessible}
              data-mp-event="Edit Changefeed Clicked"
            >
              Edit
            </Button>
            <Button
              variant="default"
              size="sm"
              color="red"
              onClick={handleDelete}
              disabled={!accessible}
              loading={deleteChangefeed.isLoading}
              data-mp-event="Delete Changefeed Clicked"
            >
              Delete
            </Button>
          </Group>
        )}
      </AccessDeniedHoverHint>

      <Modal
        opened={confirmModal.open}
        size="lg"
        onClose={() => setConfirmModal({ open: false })}
        title={confirmModal.title}
      >
        <Stack>
          {confirmModal.children}
          <Group justify="flex-end">
            <Button
              variant="default"
              onClick={() => setConfirmModal({ open: false })}
              data-mp-event="Changefeed Confirm Cancel Clicked"
            >
              Cancel
            </Button>
            <Button
              loading={pauseChangefeed.isLoading || resumeChangefeed.isLoading || deleteChangefeed.isLoading}
              onClick={confirmModal?.onConfirm}
              color="red"
              variant="light"
              data-mp-event="Changefeed Confirm Continue Clicked"
            >
              I understand, continue
            </Button>
          </Group>
        </Stack>
      </Modal>
    </>
  )
}

const ChangefeedDetailEssential = ({ cluster, changefeedId }: { cluster: SharedCluster; changefeedId: string }) => {
  const {
    store: { tenantData, activeProjectId }
  } = useStores()

  const {
    data,
    isLoading: isLoadingChangefeed,
    refetch
  } = useChangefeedServiceGetChangefeed(tenantData.id, activeProjectId, cluster.id!, changefeedId ?? '', {
    query: {
      enabled: !!cluster.id && !!changefeedId,
      refetchOnWindowFocus: false
    }
  })

  const metricsConfig = useMemo(() => getMetricsConfig(changefeedId), [changefeedId])

  const {
    data: metricsURL,
    isLoading: isLoadingMetricsURL,
    metricRequestOptions
  } = useGetMetricsQueryRangeURLForCluster(tenantData.id, activeProjectId, cluster)

  if (isLoadingChangefeed || isLoadingMetricsURL) {
    return (
      <ClusterPageShell title="Changefeed Detail" headerProps={{ withBack: true }}>
        <Loader size="xs" />
      </ClusterPageShell>
    )
  }

  if (!data) {
    return (
      <ClusterPageShell title="Changefeed Detail" headerProps={{ withBack: true }}>
        <Typography>Changefeed task not found.</Typography>
      </ClusterPageShell>
    )
  }

  return (
    <ClusterPageShell
      title="Changefeed Detail"
      headerProps={{
        withBack: true,
        rightSection: <ChangefeedDetailActions changefeed={data} refetch={refetch} cluster={cluster} />
      }}
    >
      <Group align="start" gap={24} wrap="nowrap">
        <Stack justify="flex-start" sx={{ flexGrow: 1 }}>
          <Card>
            <Stack>
              <Typography variant="title-lg">Metrics</Typography>
              <MetricsChartOnChangeFeed
                metricSourceURI={metricsURL ?? ''}
                metricInfo={metricsConfig}
                showTimeRangePicker
                metricRequestOptions={metricRequestOptions}
              />
            </Stack>
          </Card>
        </Stack>

        <Stack w={450} justify="flex-start">
          <PropertyCard title="Properties">
            <PropertyCard.Item label="Name">{data.display_name || 'no name'}</PropertyCard.Item>
            <PropertyCard.Item label="ID">{data.changefeed_id}</PropertyCard.Item>
            <PropertyCard.Item label="Status">
              <ChangefeedStatus status={data.state as any} error={data.error} />
            </PropertyCard.Item>

            {data.rcu != null && (
              <EssentialSpecificationProperty
                orgId={tenantData.id}
                projectId={activeProjectId}
                clusterId={cluster.id!}
                rcu={data.rcu}
              />
            )}

            <PropertyCard.Item label="Destination">
              {data.sink?.type ? ChangefeedNames[data.sink.type] : '--'}
            </PropertyCard.Item>
            {data.create_time && (
              <PropertyCard.Item label="Created At">
                <TimeComponent time={data.create_time} />
              </PropertyCard.Item>
            )}
            {data.checkpoint?.time && (
              <PropertyCard.Item label="Checkpoint Time">
                <TimeComponent time={data.checkpoint.time} />
              </PropertyCard.Item>
            )}
            <PropertyCard.Item label="Checkpoint TSO">{data.checkpoint?.tso || '-'}</PropertyCard.Item>

            {data.sink?.type === ChangefeedTypeEnum.KAFKA && (
              <KafkaPropertiesEssential data={data} cloudProvider={cluster.provider || ''} />
            )}

            {data.sink?.type === ChangefeedTypeEnum.MYSQL && (
              <MySQLPropertiesEssential data={data} cloudProvider={cluster.provider || ''} />
            )}
          </PropertyCard>
        </Stack>
      </Group>
    </ClusterPageShell>
  )
}

export default observer(ChangefeedDetailEssential)
