import { Divider, Paper } from '@tidbcloud/uikit'
import { useDisclosure } from '@tidbcloud/uikit/hooks'
import { observer } from 'mobx-react'
import { useEffect, useMemo } from 'react'
import { useFormContext } from 'react-hook-form'

import { useAccessCheck } from 'dbaas/contexts/access-control'
import { CloudProviderConst, getCloudProviderLabel } from 'dbaas/features/Project'
import { useCreationStore } from 'dbaas/screens/ClustersV2/new/store'
import { useGetOwnerProjects } from 'dbaas/screens/NextGen/shared/access/use-get-owner-projects'
import { ProjectEditModal } from 'dbaas/screens/OrgSettings/features/Projects/EditModal'
import { useCmekList } from 'dbaas/screens/Project/EncryptionAccess/helper'
import { useRegionServiceListRegions } from 'dbaas/services/api-clients/v2023/dedicated/v1beta1'
import useStores from 'dbaas/stores/useStores'

import { useDedicatedNodeProfiles } from './hooks/use-dedicated-node-profiles'
import { BasicSettingsSection } from './sections/BasicSettingsSection'
import { NodeSizesSection } from './sections/NodeSizesSection'
import { PortSection } from './sections/PortSection'
import { ProjectCidrSection } from './sections/ProjectCidrSection'
import { DedicatedCreationFormValue } from './types'

interface DedicatedFormProps {
  formId: string
  planSelector: React.ReactNode
  onSubmit: () => void
}

export const DedicatedForm = observer(({ formId, planSelector, onSubmit }: DedicatedFormProps) => {
  const {
    store: {
      lastVisitLocation: { orgId, projectId },
      activeProject: { aws_cmek_enabled: awsCmekEnabled }
    },
    actions: { updateLastVisitLocation, fetchUserProfileV2 }
  } = useStores()

  const ownerProjects = useGetOwnerProjects()
  const dedicatedProjects = useMemo(() => ownerProjects.filter((p) => p.type === 'dedicated'), [ownerProjects])

  const [createProjectOpened, createProjectHandlers] = useDisclosure(false)
  const canCreateProject = useAccessCheck('OrgProjectsAction')

  useEffect(() => {
    if (!dedicatedProjects.length) {
      return
    }
    const isCurrentValid = !!projectId && dedicatedProjects.some((p) => p.id === projectId)
    if (isCurrentValid) {
      return
    }
    const firstProject = dedicatedProjects[0]
    updateLastVisitLocation({ projectId: firstProject.id, orgId: firstProject.org_id ?? orgId })
  }, [dedicatedProjects, projectId, orgId, updateLastVisitLocation])

  const { dedicated: creationStore } = useCreationStore()

  const { watch, setValue, setError, clearErrors } = useFormContext<DedicatedCreationFormValue>()
  const provider = watch('dedicated.provider')
  const region = watch('dedicated.region')

  const { data: providers = [], isLoading: providerLoading } = useRegionServiceListRegions(
    { orgId, projectId, pageSize: 99 },
    {
      query: {
        enabled: !!(orgId && projectId),
        select: (data) => {
          const groups = (data?.regions ?? []).reduce<Record<string, typeof data.regions>>((acc, r) => {
            const key = r.cloudProvider ?? ''
            if (!key) return acc
            acc[key] = acc[key] ?? []
            acc[key].push(r)
            return acc
          }, {})

          return Object.keys(groups).map((p) => ({
            name: p,
            regions: (groups[p] ?? []).map((r) => ({
              name: r.regionId?.replace(/^(aws|gcp|azure)-/, '') ?? '',
              display_name: r.displayName ?? '',
              cross_account_deployment_id: ''
            }))
          }))
        },
        onSuccess: (data) => creationStore?.setProviders(data)
      }
    }
  )
  const providerOptions = useMemo(
    () =>
      providers.map((p) => ({
        value: p.name,
        label: getCloudProviderLabel(p.name),
        disabled: awsCmekEnabled && p.name === CloudProviderConst.GCP
      })),
    [awsCmekEnabled, providers]
  )

  const regions = useMemo(() => providers.find((p) => p.name === provider)?.regions ?? [], [provider, providers])

  const regionOptions = useMemo(() => regions.map((r) => ({ value: r.name, label: r.display_name })), [regions])

  const { data: cmekList } = useCmekList()
  const cmekId = useMemo(() => {
    if (!awsCmekEnabled || !provider || !region) {
      return undefined
    }
    const cmekData = cmekList?.cmeks?.find((item) => item.regionId === `${provider}-${region}`)
    return cmekData?.awsKms ? cmekData.awsKms.kmsKeyArn : cmekData?.azureKeyVault?.keyVaultKey
  }, [awsCmekEnabled, cmekList?.cmeks, provider, region])

  const {
    nodeProfiles,
    isLoading: nodeProfilesLoading,
    isError: nodeProfilesError
  } = useDedicatedNodeProfiles({
    orgId,
    projectId,
    provider,
    region,
    enabled: !!(orgId && projectId && provider && region)
  })

  useEffect(() => {
    creationStore?.setNodeProfiles(nodeProfiles)
  }, [creationStore, nodeProfiles])

  useEffect(() => {
    if (provider && region && !nodeProfilesLoading && nodeProfilesError) {
      const message = 'Failed to load node sizes. Please change region and try again.'
      setError('dedicated.sizes.tidb.profileName', { type: 'manual', message })
      setError('dedicated.sizes.tikv.profileName', { type: 'manual', message })
      return
    }

    if (!nodeProfilesError && nodeProfiles.length) {
      clearErrors(['dedicated.sizes.tidb.profileName', 'dedicated.sizes.tikv.profileName'])
    }
  }, [clearErrors, nodeProfiles.length, nodeProfilesError, nodeProfilesLoading, provider, region, setError])

  const loading = providerLoading || nodeProfilesLoading

  useEffect(() => {
    setValue('initializing.dedicated', loading ? 'true' : 'false')
  }, [loading, setValue])

  useEffect(() => {
    const currentProvider = provider
    const nextProvider = providerOptions.find((p) => !p.disabled)?.value
    if (!nextProvider) {
      return
    }
    if (!currentProvider || !providerOptions.some((p) => p.value === currentProvider && !p.disabled)) {
      setValue('dedicated.provider', nextProvider)
    }
  }, [provider, providerOptions, setValue])

  useEffect(() => {
    const currentRegion = region
    const nextRegion = regions[0]?.name ?? ''
    if (!nextRegion) {
      setValue('dedicated.region', '')
      return
    }

    if (!currentRegion || !regions.some((r) => r.name === currentRegion)) {
      setValue('dedicated.region', nextRegion)
    }
  }, [region, regions, setValue])

  useEffect(() => {
    if (!awsCmekEnabled) {
      return
    }
    setValue('dedicated.cmekId', cmekId)
  }, [awsCmekEnabled, cmekId, setValue])

  const handleProjectChange = (newProjectId: string) => {
    const nextOrgId = dedicatedProjects.find((p) => p.id === newProjectId)?.org_id
    updateLastVisitLocation({ projectId: newProjectId, orgId: nextOrgId ?? orgId })
  }

  const projectOptions = useMemo(
    () => dedicatedProjects.map((p) => ({ value: p.id, label: p.name })),
    [dedicatedProjects]
  )

  return (
    <Paper component="form" id={formId} onSubmit={onSubmit} withBorder shadow="none">
      {planSelector}

      <Divider />
      <BasicSettingsSection
        projectId={projectId}
        onProjectChange={handleProjectChange}
        projectOptions={projectOptions}
        providerOptions={providerOptions}
        regionOptions={regionOptions}
        loading={loading}
        awsCmekEnabled={!!awsCmekEnabled}
        cmekId={cmekId}
        regionRenderOptionEnabled={!!awsCmekEnabled}
        onCreateProject={createProjectHandlers.open}
        canCreateProject={canCreateProject}
      />

      <Divider />
      <NodeSizesSection provider={provider} nodeProfiles={nodeProfiles} />

      <Divider />
      <PortSection />

      <Divider />
      <ProjectCidrSection provider={provider} region={region} />

      <ProjectEditModal
        createMode="dedicated-only"
        project={undefined}
        opened={createProjectOpened}
        onClose={createProjectHandlers.close}
        onFinish={() => fetchUserProfileV2()}
      />
    </Paper>
  )
})
