import {
  Box,
  Card,
  Group,
  HoverCard,
  Loader,
  notifier,
  Progress,
  ScrollArea,
  Stack,
  Typography
} from '@tidbcloud/uikit'
import { FormTextInput } from '@tidbcloud/uikit/biz'
import { IconEdit05 } from '@tidbcloud/uikit/icons'
import { observer } from 'mobx-react'
import ms from 'ms'
import React, { useMemo, useState } from 'react'
import { FormProvider, useForm } from 'react-hook-form'
import { Link, Redirect, useParams } from 'react-router-dom'

import { humanByteSize } from 'common/services/prommetric'
import { AccessCheck } from 'dbaas/contexts/access-control'
import { DmJobStatus } from 'dbaas/features/DataMigration/JobStatus'
import { useGetDMJob, useUpdateDMJob } from 'dbaas/services/dataflow'
import { DmSubTaskStage, SubTaskStep } from 'dbaas/services/dataflow/interface'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import useStores from 'dbaas/stores/useStores'
import { timeFormatter } from 'dbaas/utils/format'

import { stageNameMapper } from '../utils'

import SelectedObjects from './SelectedObject'

const TaskProgress = ({
  progress = 0,
  currentStep,
  step,
  error,
  descriptions
}: {
  progress: number
  currentStep: SubTaskStep
  step: SubTaskStep
  error?: {
    message?: string
    workaround?: string
  }
  descriptions: string[]
}) => {
  const isCurrent = currentStep === step
  const isSuccess = progress === 100
  const isError = !isSuccess && typeof error !== 'undefined' && isCurrent
  const color = useMemo(() => {
    if (isSuccess && step !== SubTaskStep.Sync) {
      return 'green'
    }
    if (isError) {
      return 'red'
    }
    if (isCurrent) {
      return 'peacock'
    }
    return 'gray'
  }, [isSuccess, isCurrent, isError, step])

  return (
    <Stack gap={8}>
      <Group justify="space-between">
        <Typography fw={600} fz={14}>
          {stageNameMapper[step]}
        </Typography>
        {step !== SubTaskStep.Sync && <Typography fw={600} fz={14}>{`${progress}%`}</Typography>}
      </Group>

      <Progress value={progress ?? 0} color={color} />

      <Group justify="space-between" align="flex-start" wrap="nowrap">
        <Stack gap={4} maw={'50%'}>
          {error && isCurrent && (
            <>
              <Typography color="red" variant="body-md">
                {error?.message || 'Some error happened'}
              </Typography>
              <Typography color="gray" variant="body-md">
                {error?.workaround}
              </Typography>
            </>
          )}
        </Stack>

        <Stack gap={0} maw={'50%'}>
          {descriptions.map((i) => (
            <Typography key={i} variant="body-md" dangerouslySetInnerHTML={{ __html: i }} ta="right" />
          ))}
        </Stack>
      </Group>
    </Stack>
  )
}

const Property = ({ label, value }: { label: React.ReactNode; value: React.ReactNode }) => {
  return (
    <Group wrap="nowrap">
      <Typography variant="label-lg" miw={200}>
        {label}
      </Typography>
      <Typography variant="body-lg" sx={{ flex: 1 }} lh="30px">
        {value}
      </Typography>
    </Group>
  )
}

const DedicatedDmJobDetail = () => {
  const {
    store: { tenantData, activeProjectId, currentCluster }
  } = useStores()
  const { id, jobId } = useParams<{ id: string; jobId: string }>()
  const [editing, setEditing] = useState(false)
  const methods = useForm({
    mode: 'onChange',
    defaultValues: {
      name: ''
    }
  })
  const { trigger } = methods

  let { data, isLoading, isError, error, refetch } = useGetDMJob(tenantData.id, activeProjectId, id, jobId, {
    request: { timeout: 30 * 1000 },
    query: { refetchInterval: 5 * 1000 }
  })

  const changeJobName = useUpdateDMJob()

  const task = Object.values(data?.job?.sub_tasks ?? {})[0]

  const source = useMemo(() => {
    if (!task) return null
    const type = task.source?.conn_profile?.type?.toLowerCase()

    if (!type) return null
    if (type.includes('aws')) return 'AWS'
    if (type.includes('gcp') || type.includes('google')) return 'Google Cloud'
    if (type.includes('mysql')) return 'MySQL'
    return 'AWS'
  }, [task])

  const showFullMigrateProgress = data?.job?.mode === 'all' || data?.job?.mode === 'full'
  const showIncrementalProgress = data?.job?.mode === 'all' || data?.job?.mode === 'incremental'

  const onChangeJobName = async (name: string) => {
    if (!name) {
      setEditing(false)
      return
    }

    const result = await trigger('name')

    if (!result) return

    await changeJobName.mutateAsync({
      orgId: tenantData.id,
      projectId: activeProjectId,
      clusterId: id,
      id: jobId,
      data: { name }
    })

    setEditing(false)
    refetch()
  }

  if (isLoading) {
    return <Loader size="xs" />
  }

  if (isError) {
    notifier.error(getErrorMessage(error), {
      autoClose: 3000
    })
    return <Redirect to={`/clusters/${currentCluster?.id}/data-migration`} />
  }

  return (
    <FormProvider {...methods}>
      <Group align="start" gap={24} wrap="nowrap">
        <Stack justify="flex-start" sx={{ flexBasis: 450, flexShrink: 0, flexGrow: 0 }}>
          <Card withBorder shadow="none">
            <Stack gap={12}>
              <Typography variant="headline-sm">Summary</Typography>

              <Property
                label="Name"
                value={
                  editing ? (
                    <FormTextInput
                      name="name"
                      rules={{
                        maxLength: { value: 60, message: 'Should be less than 60 characters' },
                        pattern: {
                          value: /^[A-Za-z][0-9A-Za-z_-]{0,59}$/,
                          message: 'Invalid format'
                        }
                      }}
                      placeholder="Please enter the new job name"
                      autoFocus
                      size="xs"
                      onBlur={(e) => {
                        onChangeJobName(e.target.value)
                      }}
                    />
                  ) : (
                    <Group mih={30}>
                      <Typography>{data?.job?.name}</Typography>
                      <AccessCheck id="ClusterDMAction">
                        {({ accessible }) => (
                          <Box
                            onClick={accessible ? () => setEditing(true) : undefined}
                            component={IconEdit05}
                            size={14}
                            c={accessible ? 'peacock.5' : 'carbon.5'}
                            sx={{ cursor: accessible ? 'pointer' : 'not-allowed' }}
                          />
                        )}
                      </AccessCheck>
                    </Group>
                  )
                }
              />
              <Property label="Job ID" value={data?.job?.id} />
              <Property
                label="Status"
                value={
                  <Group>
                    <DmJobStatus status={task?.stage!} error={task?.process_error?.message} />
                  </Group>
                }
              />
              <Property
                label="Data Source"
                value={
                  <Group>
                    <Typography variant="body-lg">{task.source?.conn_profile?.type}</Typography>

                    <HoverCard withArrow shadow="md">
                      <HoverCard.Target>
                        <a>Details</a>
                      </HoverCard.Target>

                      <HoverCard.Dropdown>
                        <Stack gap={0}>
                          <Property label="Type" value={source} />
                          <Property label="Connectivity Method" value={task.source?.conn_profile?.conn_method} />
                          {task.source?.conn_profile?.endpoint_service_name && (
                            <Property
                              label="Endpoint Service Name"
                              value={task.source?.conn_profile?.endpoint_service_name}
                            />
                          )}
                          <Property label="Hostname or IP address" value={task.source?.conn_profile?.host} />
                          <Property label="Port" value={task.source?.conn_profile?.port} />
                          <Property label="User" value={task.source?.conn_profile?.user} />
                        </Stack>
                      </HoverCard.Dropdown>
                    </HoverCard>
                  </Group>
                }
              />
              <Property
                label="Data Target"
                value={
                  <Group>
                    <Typography variant="body-lg">TiDB</Typography>
                    <HoverCard withArrow shadow="md">
                      <HoverCard.Target>
                        <a>Details</a>
                      </HoverCard.Target>

                      <HoverCard.Dropdown>
                        <Stack gap={0}>
                          <Property label="Type" value="TiDB Cloud" />
                          <Property label="Region" value={currentCluster?.regionDisplayName} />
                          <Property
                            label="Name"
                            value={
                              <Link to={`/clusters/${currentCluster?.id}/overview`}>{currentCluster?.displayName}</Link>
                            }
                          />
                          <Property label="ID" value={currentCluster?.id} />
                          <Property label="Port" value={currentCluster?.dedicated?.port} />
                          <Property label="User" value={data?.job?.cluster_user || '-'} />
                        </Stack>
                      </HoverCard.Dropdown>
                    </HoverCard>
                  </Group>
                }
              />

              <Property
                label="Migration Objects"
                value={
                  task.source?.ba_rules?.do_dbs?.length || !!task.source?.ba_rules?.do_tables?.length ? (
                    <HoverCard withArrow shadow="md" width={400}>
                      <HoverCard.Target>
                        <a>Details</a>
                      </HoverCard.Target>

                      <HoverCard.Dropdown>
                        <ScrollArea.Autosize mah={300}>
                          <SelectedObjects rules={task?.source?.ba_rules} />
                        </ScrollArea.Autosize>
                      </HoverCard.Dropdown>
                    </HoverCard>
                  ) : (
                    'All'
                  )
                }
              />

              {task.source?.binlog_gtid && <Property label="Start Position" value={task.source?.binlog_gtid} />}
              {task.source?.binlog_name && task.source?.binlog_pos && (
                <Property label="Start Position" value={[task.source?.binlog_name, task.source.binlog_pos].join('_')} />
              )}

              <Property label="Creation Time" value={timeFormatter(data?.job?.create_time!, tenantData.timezone)} />
            </Stack>
          </Card>

          <Card withBorder shadow="none">
            <Stack>
              <Typography variant="headline-sm">Specification</Typography>

              {task.stage === DmSubTaskStage.Scaling && data?.job?.modification ? (
                <Group wrap="nowrap">
                  <Box
                    sx={(theme) => ({
                      backgroundColor: theme.colors.carbon[2],
                      padding: '8px 24px',
                      borderRadius: 8,
                      width: '100%'
                    })}
                  >
                    <Group>
                      <Loader size="xs" />
                      <Typography
                        fz={14}
                        fw={500}
                      >{`Scaling from ${data.job.modification.from_rcu} RCUs to ${data.job.modification.to_rcu} RCUs`}</Typography>
                    </Group>
                  </Box>
                </Group>
              ) : (
                <Group wrap="nowrap">
                  <Box
                    sx={(theme) => ({
                      backgroundColor: theme.colors.carbon[2],
                      padding: '8px 24px',
                      borderRadius: 8,
                      flexShrink: 0
                    })}
                  >
                    <Typography fw={700} fz={18} color="carbon.7">
                      {data?.job?.spec?.capacity_unit + ' RCUs'}
                    </Typography>
                  </Box>
                  <Typography color="carbon.7" fz={12} lineClamp={2}>
                    {`The maximum performance are ${data?.job?.spec?.max_full_phase_speed} MiB/s for existing data and ${data?.job?.spec?.max_incr_phase_speed} rows/s for incremental data migration.`}
                  </Typography>
                </Group>
              )}
            </Stack>
          </Card>
        </Stack>

        <Stack justify="flex-start" style={{ flexGrow: 1 }}>
          <Card withBorder shadow="none">
            <Stack>
              <Typography variant="headline-sm">Progress</Typography>

              {showFullMigrateProgress && (
                <>
                  <TaskProgress
                    currentStep={task.current_step!}
                    step={SubTaskStep.Dump}
                    progress={task.dump_detail?.progress!}
                    error={task.process_error}
                    descriptions={[
                      `${humanByteSize(Number(task.dump_detail?.finished_bytes ?? '0'), false)} data exported`,
                      `Duration ${ms(Number(task.dump_detail?.duration ?? '0') * 1000)}, ${
                        task.dump_detail?.progress === 100 ? 'Avg throughput' : 'Current throughput'
                      } ${humanByteSize(
                        task.dump_detail?.progress === 100
                          ? Math.round(
                              Number(task.dump_detail?.finished_bytes ?? '0') /
                                Number(task.dump_detail?.duration ?? '1')
                            )
                          : Number(task.dump_detail?.bps ?? '0'),
                        false
                      )}/s`
                    ]}
                  />

                  <TaskProgress
                    currentStep={task.current_step!}
                    step={SubTaskStep.Load}
                    progress={task.load_detail?.progress!}
                    error={task.process_error}
                    descriptions={[
                      `${humanByteSize(Number(task.load_detail?.finished_bytes ?? '0'), false)} data imported`,
                      `Duration ${ms(Number(task.load_detail?.duration ?? '0') * 1000)}, ${
                        task.load_detail?.progress === 100 ? 'Avg throughput' : 'Current throughput'
                      } ${humanByteSize(
                        task.dump_detail?.progress === 100
                          ? Math.round(
                              Number(task.load_detail?.finished_bytes ?? '0') /
                                Number(task.load_detail?.duration ?? '1')
                            )
                          : Number(task.load_detail?.bps ?? '0'),
                        false
                      )}/s`
                    ]}
                  />
                </>
              )}

              {showIncrementalProgress && (
                <TaskProgress
                  currentStep={task.current_step!}
                  step={SubTaskStep.Sync}
                  progress={
                    task.current_step === SubTaskStep.Sync
                      ? Number(task.sync_detail?.latency ?? '0') > 0
                        ? 95
                        : 100
                      : 0
                  }
                  error={task.process_error}
                  descriptions={
                    [
                      `Latency ${ms(Number(task.sync_detail?.latency ?? '0') * 1000)}`,
                      task?.sync_detail?.checkpoint ? `Checkpoint <b>${task?.sync_detail?.checkpoint}</b>` : null
                    ].filter(Boolean) as string[]
                  }
                />
              )}
            </Stack>
          </Card>
        </Stack>
      </Group>
    </FormProvider>
  )
}

export default observer(DedicatedDmJobDetail)
