import { Alert, Divider, Group, HoverCard, Loader, Modal, notifier, Radio, Stack, Typography } from '@tidbcloud/uikit'
import { Form, FormErrorMessage, FormSelect, FormTextInput } from '@tidbcloud/uikit/biz'
import { useDisclosure } from '@tidbcloud/uikit/hooks'
import { IconInfoCircle } from '@tidbcloud/uikit/icons'
import { useEffect, useMemo, useState } from 'react'
import { useForm } from 'react-hook-form'

import { ModalCodeTitle } from 'dbaas/components'
import { markOneConsoleFeedbackEventCompleted } from 'dbaas/layouts/v5/NewNavGuide/feedbackState'
import { ProjectEditModal } from 'dbaas/screens/OrgSettings/features/Projects/EditModal'
import { useListOrgProjects, useListProjects } from 'dbaas/services'
import { useTiDBServiceMoveTiDB } from 'dbaas/services/api-clients/v2023/cluster-management/v1beta2'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import useStores from 'dbaas/stores/useStores'
import { MixpanelEvents } from 'dbaas/utils/tracking/event'
import { tidbDisplayNameRules } from 'dbaas/utils/validationRules'

import {
  DEFAULT_MOVE_SUCCESS_MESSAGE,
  isMoveNamingConflict,
  MOVE_CONFLICT_RETRY_MESSAGE,
  PROJECT_PLACEMENT_TOOLTIP
} from './helpers'

type MoveMode = 'move' | 'rename'
type MovePlacementType = 'project' | 'outOfProject'

interface MoveProjectOption {
  value: string
  label: string
}

interface MoveFormValues {
  placementType: MovePlacementType
  targetProjectId: string
  name: string
}

interface MoveInstance {
  tidbId: string
  currentProjectId: string
  displayName: string
  servicePlan?: string
}

interface MoveInstanceModalProps {
  opened: boolean
  onClose: () => void
  onFinish?: () => Promise<unknown> | void
  instance: MoveInstance
}

const getDefaultTargetProjectId = (projectOptions: MoveProjectOption[]) => projectOptions[0]?.value || ''

const getDefaultMoveFormValues = (projectOptions: MoveProjectOption[]): MoveFormValues => ({
  placementType: 'outOfProject',
  targetProjectId: getDefaultTargetProjectId(projectOptions),
  name: ''
})

const getNextTargetProjectId = (targetProjectId: string, projectOptions: MoveProjectOption[]) => {
  return projectOptions.some((option) => option.value === targetProjectId)
    ? targetProjectId
    : getDefaultTargetProjectId(projectOptions)
}

export const MoveInstanceModal = ({ opened, onClose, onFinish, instance }: MoveInstanceModalProps) => {
  const { tidbId, currentProjectId, displayName: instanceName, servicePlan } = instance

  const {
    store: {
      userProfile: { id: userId },
      lastVisitLocation: { orgId }
    },
    actions: { updateLastVisitLocation }
  } = useStores()

  const [mode, setMode] = useState<MoveMode>('move')
  const [conflictTargetProjectId, setConflictTargetProjectId] = useState('')

  const projectsQuery = useListProjects(
    orgId,
    { page: 1, per_page: 1000, types: ['tidbx'] },
    {
      query: {
        enabled: opened && !!orgId,
        refetchOnWindowFocus: false,
        select: (data) => data.items?.filter((project) => project.type === 'tidbx') || []
      }
    }
  )

  const projectOptions = useMemo<MoveProjectOption[]>(() => {
    return (
      projectsQuery.data
        ?.filter((project) => project.id && project.id !== currentProjectId)
        .map((project) => ({
          value: project.id!,
          label: project.name || project.id!
        })) || []
    )
  }, [currentProjectId, projectsQuery.data])

  const hasTargetProjects = projectOptions.length > 0

  const hiddenProjectQuery = useListOrgProjects(
    orgId,
    { types: ['tidbx_virtual'] },
    {
      query: {
        enabled: opened && !!orgId,
        refetchOnWindowFocus: false,
        select: (data) => data.items?.[0]?.id || ''
      }
    }
  )
  const hiddenProjectId = hiddenProjectQuery.data || ''
  const isInHiddenProject = !!hiddenProjectId && currentProjectId === hiddenProjectId

  const [createProjectOpened, createProjectHandlers] = useDisclosure()

  const form = useForm<MoveFormValues>({
    mode: 'onChange',
    defaultValues: getDefaultMoveFormValues(projectOptions)
  })
  const { watch, reset, setFocus, setValue, getValues } = form

  const placementType = watch('placementType')
  const targetProjectId = watch('targetProjectId')
  const renameValue = watch('name')
  const renameMode = mode === 'rename'

  const moveMutation = useTiDBServiceMoveTiDB({
    mutation: {
      onSuccess: async (_response, variables) => {
        updateLastVisitLocation({ projectId: variables.data.targetProjectId })
        markOneConsoleFeedbackEventCompleted(userId, 'move')

        await onFinish?.()
        onClose()
        notifier.success(DEFAULT_MOVE_SUCCESS_MESSAGE)
      },
      onError: (error, variables) => {
        if (!isMoveNamingConflict(error)) {
          return
        }

        setMode('rename')
        setConflictTargetProjectId(variables.data.targetProjectId)

        if (!renameMode) {
          setValue('name', variables.data.name?.trim() || instanceName)
        }
      }
    }
  })
  const isSubmitting = moveMutation.isLoading

  useEffect(() => {
    if (!opened || placementType !== 'project') {
      return
    }

    const nextTargetProjectId = getNextTargetProjectId(getValues('targetProjectId'), projectOptions)

    if (nextTargetProjectId !== getValues('targetProjectId')) {
      setValue('targetProjectId', nextTargetProjectId)
    }
  }, [getValues, opened, placementType, projectOptions, setValue])

  useEffect(() => {
    if (renameMode && opened) {
      setFocus('name')
    }
  }, [opened, renameMode, setFocus])

  const clearRenameConflictState = () => {
    if (!renameMode) {
      return
    }

    setMode('move')
    setConflictTargetProjectId('')
    setValue('name', '')
  }

  const resetModalState = () => {
    setMode('move')
    setConflictTargetProjectId('')
    reset(getDefaultMoveFormValues(projectOptions))
    moveMutation.reset()
  }

  const handleClose = () => {
    if (isSubmitting) {
      return
    }

    onClose()
  }

  const handlePlacementChange = (value: string) => {
    const nextPlacementType = value as MovePlacementType

    if (renameMode && nextPlacementType !== placementType) {
      clearRenameConflictState()
    }

    setValue('placementType', nextPlacementType)

    if (nextPlacementType === 'project') {
      setValue('targetProjectId', getNextTargetProjectId(getValues('targetProjectId'), projectOptions))
    }
  }

  const handleSubmit = async (values: MoveFormValues) => {
    const trimmedName = values.name?.trim()
    const resolvedTargetProjectId = values.placementType === 'outOfProject' ? hiddenProjectId : values.targetProjectId

    return moveMutation.mutateAsync({
      tidbId,
      data: {
        targetProjectId: resolvedTargetProjectId,
        ...(renameMode && trimmedName ? { name: trimmedName } : {})
      },
      params: {
        orgId
      }
    })
  }

  const submitDisabled =
    (placementType === 'outOfProject' && !hiddenProjectId) ||
    (placementType === 'project' && !targetProjectId) ||
    (renameMode && !renameValue.trim())

  const handleSubmitError = (error?: unknown) => {
    if (isMoveNamingConflict(error)) {
      return MOVE_CONFLICT_RETRY_MESSAGE
    }

    return getErrorMessage(error, true)
  }

  return (
    <>
      <Modal
        opened={opened}
        onClose={handleClose}
        onExitTransitionEnd={resetModalState}
        centered
        trapFocus={false}
        closeOnClickOutside={false}
        closeOnEscape={!isSubmitting}
        size={640}
        title={
          <>
            Move <ModalCodeTitle maxWidth={400}>{instanceName}</ModalCodeTitle>
          </>
        }
        data-testid="move-instance-modal"
      >
        <Form
          form={form}
          onSubmit={handleSubmit}
          onCancel={handleClose}
          onError={handleSubmitError}
          actionsProps={{
            confirmText: renameMode ? 'Rename and Move' : 'Move',
            cancelText: 'Cancel',
            confirmProps: {
              loading: isSubmitting,
              disabled: submitDisabled,
              'data-testid': 'move-instance-modal-submit',
              'data-mp-event': MixpanelEvents.TidbResource.Move.SubmitClicked,
              ...(servicePlan ? { 'data-mp-service-plan': servicePlan } : {})
            },
            cancelProps: {
              disabled: isSubmitting,
              'data-testid': 'move-instance-modal-cancel',
              'data-mp-event': MixpanelEvents.TidbResource.Move.CancelClicked,
              ...(servicePlan ? { 'data-mp-service-plan': servicePlan } : {})
            }
          }}
        >
          <Stack>
            {isInHiddenProject && (
              <Alert data-testid="move-instance-modal-already-outside-alert">
                This instance is already outside any project.
              </Alert>
            )}

            <Radio.Group
              name="placementType"
              label="Where do you want to move this instance?"
              value={placementType}
              onChange={handlePlacementChange}
              styles={{
                label: {
                  marginBottom: 8
                }
              }}
              data-testid="move-instance-modal-placement-radio-group"
            >
              <Group align="center" gap="xl" wrap="nowrap">
                <Radio
                  value="outOfProject"
                  label="Outside any project"
                  disabled={!hiddenProjectId || isInHiddenProject}
                  data-testid="move-instance-modal-out-of-project"
                />
                <Group wrap="nowrap" gap={8}>
                  <Radio value="project" label="To a project" data-testid="move-instance-modal-in-project" />
                  <HoverCard withArrow withinPortal shadow="md">
                    <HoverCard.Target>
                      <IconInfoCircle size={16} c="carbon.7" sx={{ cursor: 'pointer' }} />
                    </HoverCard.Target>
                    <HoverCard.Dropdown maw={320}>{PROJECT_PLACEMENT_TOOLTIP}</HoverCard.Dropdown>
                  </HoverCard>
                </Group>
              </Group>
            </Radio.Group>

            {placementType === 'project' && (
              <FormSelect
                name="targetProjectId"
                data={projectOptions}
                placeholder="Select a project"
                searchable
                disabled={projectsQuery.isLoading}
                rightSection={projectsQuery.isLoading ? <Loader size="xs" /> : undefined}
                nothingFoundMessage={
                  <>
                    <Typography variant="body-lg" c="carbon.6" pb="sm">
                      No projects available
                    </Typography>
                    <Divider />
                    <Typography
                      variant="action-md"
                      onClick={() => createProjectHandlers.open()}
                      pt="sm"
                      sx={{ fontWeight: 500, cursor: 'pointer' }}
                      data-testid="move-instance-modal-create-project"
                    >
                      + Create a Project
                    </Typography>
                  </>
                }
                onChange={(value) => {
                  if (renameMode && value && value !== conflictTargetProjectId) {
                    clearRenameConflictState()
                  }
                }}
                data-testid="move-instance-modal-project-select"
              />
            )}

            {renameMode && (
              <FormTextInput
                name="name"
                label="New instance name"
                placeholder="Enter a new instance name"
                autoFocus
                rules={tidbDisplayNameRules}
                data-testid="move-instance-modal-rename-input"
              />
            )}

            {hiddenProjectQuery.isError && placementType === 'outOfProject' && (
              <FormErrorMessage message="Couldn't load the default placement. Please try again later." closable />
            )}

            {projectsQuery.isError && !hasTargetProjects && placementType === 'project' && (
              <FormErrorMessage message="Couldn't load available projects. Please try again later." closable />
            )}
          </Stack>
        </Form>
      </Modal>

      <ProjectEditModal
        createMode="tidbx-only"
        project={undefined}
        opened={createProjectOpened}
        onClose={createProjectHandlers.close}
        onFinish={() => {
          projectsQuery.refetch()
        }}
      />
    </>
  )
}
