import {
  Anchor,
  Box,
  Card,
  Center,
  Flex,
  Group,
  HoverCard,
  Loader,
  notifier,
  SimpleGrid,
  Stack,
  Typography,
  useMantineTheme
} from '@tidbcloud/uikit'
import { IconServer03 } from '@tidbcloud/uikit/icons'
import { useUpdateEffect } from 'ahooks'
import { capitalize } from 'lodash-es'
import { TransformNullValue } from 'metrics-chart'
import { observer } from 'mobx-react'
import { ReactNode } from 'react'
import { useHistory, useParams } from 'react-router-dom'
import ReactFlow, {
  BaseEdge,
  EdgeProps,
  getStraightPath,
  Handle,
  MarkerType,
  NodeProps,
  Position,
  useEdgesState,
  useNodesState
} from 'reactflow'
import 'reactflow/dist/style.css'
import useResizeObserver from 'use-resize-observer'

import { MetricInfo } from 'dbaas/components/ChartCard/helpers'
import TimeComponent from 'dbaas/components/TimeComponent'
import { SharedCluster, useSharedCluster } from 'dbaas/features/ClusterManagement/cluster'
import { RecoveryGroupStatus } from 'dbaas/features/RecoveryGroup'
import { MetricsChartOnChangeFeed } from 'dbaas/screens/Changefeed/ConnectorCard/MetricsChart'
import { ProjectPageShell } from 'dbaas/screens/ConsoleV2/components'
import { useClusterServiceGetCluster } from 'dbaas/services/api-clients/v2023/cluster-management/v1beta1'
import { useGetRecoveryGroup } from 'dbaas/services/dataflow'
import {
  V1beta1GetRecoveryGroupResp,
  V1beta1RecoveryGroupState,
  V1beta1RgClusterComponentState,
  V1beta1RgClusterInfo,
  V1beta1RgClusterRole,
  V1beta1RgLinkMetricsLabel,
  V1beta1RgReplicationInfo,
  V1beta1RgReplicationState
} from 'dbaas/services/dataflow/interface'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import { useGetMetricsQueryRangeURLForCluster } from 'dbaas/services/useO11YQuery'
import useStores from 'dbaas/stores/useStores'

import { ROOT_ROUTE } from '../helper'
import { useRecoveryGroupActions } from '../List/RecoveryGroupAction'

const PropertyItem = ({ label, value }: { label: string; value: ReactNode }) => {
  return (
    <Group gap="md" wrap="nowrap" mih={36}>
      <Typography variant="label-lg" miw={150}>
        {label}
      </Typography>
      <Typography variant="body-lg" sx={{ wordBreak: 'break-all' }}>
        {value}
      </Typography>
    </Group>
  )
}

const getMetricsConfig = (metricsLabel: V1beta1RgLinkMetricsLabel[]): MetricInfo[] => {
  return [
    {
      title: 'Latency',
      queries: metricsLabel.map((i) => ({
        promql: `max(ticdc_owner_checkpoint_ts_lag{changefeed="${i.changefeed}"}) by (changefeed)`,
        name: 'changefeed',
        type: 'line'
      })),
      nullValue: TransformNullValue.AS_ZERO,
      unit: 's'
    },
    {
      title: 'Throughput',
      queries: metricsLabel.map((i) => ({
        promql: `sum(rate(ticdc_sink_write_bytes_total{changefeed=~"${i.changefeed}"}[1m])) by (changefeed)`,
        name: 'changefeed',
        type: 'line'
      })),
      nullValue: TransformNullValue.AS_ZERO,
      unit: 'short'
    }
  ]
}

const CustomClusterNode = ({ data }: NodeProps<V1beta1RgClusterInfo>) => {
  const theme = useMantineTheme()
  const color = (() => {
    return {
      [V1beta1RgClusterComponentState.HEALTHY]: 'green',
      [V1beta1RgClusterComponentState.BROKEN]: 'red',
      [V1beta1RgClusterComponentState.ACTIONING]: 'peacock',
      [V1beta1RgClusterComponentState.RG_CLUSTER_COMPONENT_STATE_UNSPECIFIED]: 'gray'
    }[data.state!]
  })()
  const borderColor = theme.colors[color]?.[7]
  const bgColor = theme.colors[color]?.[3]
  const iconColor = theme.colors[color]?.[9]
  const handleStyle = {
    borderColor,
    backgroundColor: borderColor,
    minHeight: 2,
    minWidth: 2,
    height: 2,
    width: 2,
    borderRadius: 0
  }

  return (
    <>
      <Stack align="center">
        <Stack gap={0} align="center">
          <Typography variant="label-md" maw={150} truncate>
            {data.display_name}
          </Typography>
          <Typography variant="body-md">({`${data.region?.provider?.name ?? 'aws'}, ${data.region?.name}`})</Typography>
          <Typography variant="body-md">{`${capitalize(data.role)}, ${capitalize(data.state)}`}</Typography>
        </Stack>
        <Box
          sx={{
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            position: 'relative',
            borderRadius: '50%',
            height: 48,
            width: 48,
            borderWidth: 2,
            borderStyle: data.role === V1beta1RgClusterRole.PRIMARY ? 'solid' : 'dotted',
            borderColor: borderColor,
            backgroundColor: bgColor
          }}
        >
          <IconServer03 size={24} color={iconColor} />
          {data.role === V1beta1RgClusterRole.PRIMARY && (
            <Handle type="source" position={Position.Right} id={data.id} style={handleStyle} />
          )}
          {data.role === V1beta1RgClusterRole.SECONDARY && (
            <Handle type="target" position={Position.Left} id={data.id} style={handleStyle} />
          )}
        </Box>
      </Stack>
    </>
  )
}

const nodeTypes = {
  cluster: CustomClusterNode
}

const CustomEdge = ({
  id,
  sourceX,
  sourceY,
  targetX,
  targetY,
  data,
  markerEnd
}: EdgeProps<V1beta1RgReplicationInfo & { color: string }>) => {
  const [edgePath] = getStraightPath({
    sourceX,
    sourceY,
    targetX,
    targetY
  })

  return (
    <>
      <BaseEdge id={id} path={edgePath} markerEnd={markerEnd} style={{ stroke: data?.color, strokeWidth: 2 }} />
    </>
  )
}

const edgeTypes = {
  replication: CustomEdge
}

const LoadingIndicator = ({ text }: { text: string }) => {
  return (
    <Center sx={{ flexDirection: 'column', gap: 16, height: '100%', minHeight: 192 }}>
      <Loader />
      <Typography variant="body-lg">{text}</Typography>
    </Center>
  )
}

const Detail = ({ data, cluster }: { data: V1beta1GetRecoveryGroupResp; cluster: SharedCluster }) => {
  const theme = useMantineTheme()
  const metricsConfig = getMetricsConfig(data.topology?.replications?.[0]?.metrics_labels ?? [])
  const { ref, width = 1, height = 1 } = useResizeObserver<HTMLDivElement>()
  const dbCount = data.setting?.table_filters?.length ?? 0
  const isAll = dbCount === 1 && data.setting?.table_filters?.[0] === '*.*'
  const isCreating = data.state === V1beta1RecoveryGroupState.CREATING

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

  const [nodes, setNodes] = useNodesState([])
  const [edges, setEdges] = useEdgesState([])

  useUpdateEffect(() => {
    const nodes =
      data.topology?.clusters?.map((node, index) => {
        return {
          id: node.id!,
          type: 'cluster',
          position: { x: (width / 3) * (index + 1) - 100, y: height / 2 - 80 },
          data: { ...node, label: node.display_name }
        }
      }) ?? []

    const edges =
      data.topology?.replications?.map((relation) => {
        const { source, dest } = relation
        const color = (() => {
          return {
            [V1beta1RgReplicationState.REPLICATION_HEALTHY]: 'green',
            [V1beta1RgReplicationState.REPLICATION_BROKEN]: 'red',
            [V1beta1RgReplicationState.REPLICATION_MISSING]: 'red',
            [V1beta1RgReplicationState.REPLICATION_ACTIONING]: 'peacock',
            [V1beta1RgReplicationState.RG_REPLICATION_STATE_UNSPECIFIED]: undefined
          }[relation.state!]
        })()
        const edgeColor = theme.colors?.[color!]?.[7]

        return {
          id: `${source}->${dest}`,
          type: 'replication',
          source: source!,
          target: dest!,
          data: { ...relation, color: edgeColor },
          markerEnd: {
            type: MarkerType.Arrow,
            color: edgeColor
          }
        }
      }) ?? []

    setNodes(nodes)
    setEdges(edges)
  }, [width, data.topology])

  return (
    <Stack>
      <Flex align="stretch" gap={24}>
        <Card w={460}>
          <Typography variant="headline-md" mb={16}>
            Summary
          </Typography>
          <Stack gap={0}>
            <PropertyItem label="Name" value={data.name} />
            <PropertyItem label="ID" value={data.id} />
            <PropertyItem label="Status" value={<RecoveryGroupStatus status={data.state!} />} />
            <PropertyItem label="Resiliency Policy" value="No consistency guarantee" />
            <PropertyItem
              label="Assigned Databases"
              value={
                isAll ? (
                  'All'
                ) : (
                  <Group>
                    <Typography>{`${dbCount} database${dbCount === 1 ? '' : 's'}`}</Typography>
                    <HoverCard position="right" shadow="xs">
                      <HoverCard.Target>
                        <Anchor>View Detail</Anchor>
                      </HoverCard.Target>
                      <HoverCard.Dropdown>
                        <Stack gap={8}>
                          {data.setting?.table_filters?.map((i, index) => (
                            <Typography key={index}>{i.split('.')?.[0]}</Typography>
                          ))}
                        </Stack>
                      </HoverCard.Dropdown>
                    </HoverCard>
                  </Group>
                )
              }
            />
            <PropertyItem label="Created at" value={<TimeComponent time={data.create_time ?? ''} />} />
            <PropertyItem label="Created by" value={data.creator} />
          </Stack>
        </Card>

        <Card sx={{ flexGrow: 1, display: 'flex', flexDirection: 'column' }}>
          <Typography variant="headline-md" mb={16}>
            Topology
          </Typography>

          <Box sx={{ position: 'relative', flexGrow: 1, '& .react-flow__pane': { cursor: 'default' } }} ref={ref}>
            {data.topology ? (
              <ReactFlow
                defaultViewport={{ x: 0, y: 0, zoom: 1 }}
                nodes={nodes}
                edges={edges}
                nodeTypes={nodeTypes}
                edgeTypes={edgeTypes}
                nodesDraggable={false}
                nodesConnectable={false}
                nodesFocusable={false}
                edgesFocusable={false}
                elementsSelectable={false}
                autoPanOnNodeDrag={false}
                panOnDrag={false}
                selectionOnDrag={false}
                zoomOnScroll={false}
                zoomOnPinch={false}
                selectNodesOnDrag={false}
                elevateNodesOnSelect={false}
                minZoom={1}
                maxZoom={1}
                zoomOnDoubleClick={false}
              />
            ) : (
              <LoadingIndicator text="loading map" />
            )}
          </Box>
        </Card>
      </Flex>

      <Card w="100%">
        <Typography variant="headline-md" mb={16}>
          Metrics
        </Typography>
        <SimpleGrid cols={2}>
          {isCreating || isLoadingMetricsURL ? (
            <>
              <Card p={16} bg="carbon.2" shadow="none">
                <LoadingIndicator text="loading graph" />
              </Card>
              <Card p={16} bg="carbon.2" shadow="none">
                <LoadingIndicator text="loading graph" />
              </Card>
            </>
          ) : (
            <MetricsChartOnChangeFeed
              metricSourceURI={metricsURL ?? ''}
              metricInfo={metricsConfig}
              metricRequestOptions={metricRequestOptions}
            />
          )}
        </SimpleGrid>
      </Card>
    </Stack>
  )
}

export const RecoveryGroupDetail = observer(() => {
  const history = useHistory()
  const {
    store: { tenantData, activeProjectId }
  } = useStores()
  const { id } = useParams<{ id: string; jobId: string }>()

  const {
    data,
    isLoading: isLoadingRg,
    error,
    refetch
  } = useGetRecoveryGroup(id, {
    org_id: tenantData.id,
    project_id: activeProjectId
  })

  const { data: clusterData, isLoading: isLoadingPrimaryCluster } = useClusterServiceGetCluster(
    data?.setting?.primary!,
    {
      orgId: tenantData.id,
      projectId: activeProjectId
    },
    {
      query: {
        refetchOnWindowFocus: false
      }
    }
  )
  const sharedCluster = useSharedCluster(clusterData ?? null)
  const isLoading = isLoadingRg || isLoadingPrimaryCluster
  const { modals, ActionButton } = useRecoveryGroupActions(data!, tenantData.id, activeProjectId, refetch)

  const goBack = () => {
    history.push(ROOT_ROUTE)
  }

  if (error) {
    notifier.error(getErrorMessage(error))
    goBack()
    return null
  }

  return (
    <ProjectPageShell
      title="Recovery Group Detail"
      headerProps={{
        withBack: true,
        onBackClick: goBack,
        rightSection: <ActionButton data={data!} position="bottom-end" />
      }}
    >
      {isLoading ? <Loader size="xs" /> : <Detail data={data!} cluster={sharedCluster!} />}
      {modals}
    </ProjectPageShell>
  )
})
