import {
  ActionIcon,
  Alert,
  Button,
  Divider,
  Group,
  Select,
  Stack,
  Textarea,
  TextInput,
  Typography
} from '@tidbcloud/uikit'
import { IconMinusCircle, IconPlus } from '@tidbcloud/uikit/icons'
import React, { useEffect, useMemo } from 'react'

import {
  cloudConfigControllerListAvailableComponentVersions,
  cloudConfigControllerListComponents,
  cloudConfigControllerListTemplates
} from 'apiClient/services/devops'
import {
  CloudConfigControllerListAvailableComponentVersionsClusterType,
  CloudconfigClusterType,
  CloudconfigTemplate
} from 'apiClient/services/devops/interface'

export type ComponentSetting = {
  image_tag?: string
  key?: string
  version?: string
}

export type ConfigTemplateSetting = {
  key?: string
  value?: number
}

export interface CloudconfigCreateMetaProfileForm {
  cluster_type: CloudconfigClusterType
  component_settings?: ComponentSetting[]
  config_custom_templates?: ConfigTemplateSetting[]
  description?: string
  profile_name: string
  tenant_id: string
}

export type CloudconfigCreateMetaProfileFormErrors = Partial<
  Record<string, string>
>

export const getInitialMetaProfileForm = ({
  clusterType,
  tenantId
}: {
  clusterType: CloudconfigClusterType
  tenantId: string
}): CloudconfigCreateMetaProfileForm => ({
  cluster_type: clusterType,
  component_settings: [],
  config_custom_templates: [],
  profile_name: '',
  tenant_id: tenantId
})

const getOptionData = (values: string[]) =>
  values.map((value) => ({
    label: value,
    value
  }))

const getSelectedKeys = (items: Array<{ key?: string }> | undefined) =>
  (items || []).map((item) => item.key).filter(Boolean) as string[]

const getAvailableOptionData = (
  values: string[],
  selectedValues: string[],
  currentValue?: string
) =>
  getOptionData(
    values.filter(
      (value) => value === currentValue || !selectedValues.includes(value)
    )
  )

const SectionTitle = ({ children }: { children: React.ReactNode }) => (
  <Group gap="md" align="center" wrap="nowrap">
    <Divider style={{ flex: 1 }} />
    <Typography variant="title-md" style={{ whiteSpace: 'nowrap' }}>
      {children}
    </Typography>
    <Divider style={{ flex: 1 }} />
  </Group>
)

type ComponentSettingFormItemProps = {
  clusterType: CloudconfigClusterType
  componentNames: string[]
  errors: CloudconfigCreateMetaProfileFormErrors
  index: number
  onChange: (setting: ComponentSetting) => void
  onRemove: () => void
  selectedComponentNames: string[]
  setting: ComponentSetting
  tenantId: string
}

function ComponentSettingFormItem({
  clusterType,
  componentNames,
  errors,
  index,
  onChange,
  onRemove,
  selectedComponentNames,
  setting,
  tenantId
}: ComponentSettingFormItemProps) {
  const [versions, setVersions] = React.useState<string[]>([])
  const [loadingVersions, setLoadingVersions] = React.useState(false)

  useEffect(() => {
    if (!setting.key) {
      setVersions([])
      return
    }

    let mounted = true
    setLoadingVersions(true)
    ;(async () => {
      try {
        const resp = (
          await cloudConfigControllerListAvailableComponentVersions(
            setting.key!,
            {
              cluster_type:
                clusterType as CloudConfigControllerListAvailableComponentVersionsClusterType,
              tenant_id: tenantId
            }
          )
        ).data

        if (mounted) {
          setVersions(resp.versions || [])
        }
      } catch (e) {
        console.error('fetch component versions failed', e)
        if (mounted) {
          setVersions([])
        }
      } finally {
        if (mounted) {
          setLoadingVersions(false)
        }
      }
    })()

    return () => {
      mounted = false
    }
  }, [clusterType, setting.key, tenantId])

  return (
    <Group align="flex-start" gap="sm" wrap="nowrap">
      <Select
        data={getAvailableOptionData(
          componentNames,
          selectedComponentNames,
          setting.key
        )}
        error={errors[`component_settings.${index}.key`]}
        label="Component name"
        onChange={(nextComponent) =>
          onChange({
            key: nextComponent || undefined,
            image_tag: undefined,
            version: undefined
          })
        }
        placeholder="Select component"
        required
        searchable
        style={{ width: 200 }}
        value={setting.key || null}
      />
      <Select
        data={getOptionData(versions)}
        disabled={!setting.key}
        error={errors[`component_settings.${index}.version`]}
        label="Version"
        onChange={(nextVersion) =>
          onChange({
            ...setting,
            version: nextVersion || undefined
          })
        }
        placeholder={
          !setting.key
            ? 'select component first'
            : loadingVersions
            ? 'loading versions'
            : 'component version'
        }
        required
        searchable
        style={{ width: 220 }}
        value={setting.version || null}
      />
      <TextInput
        description="Optional. If omitted, the latest image tag for the selected version will be used."
        label="Image tag"
        onChange={(event) =>
          onChange({
            ...setting,
            image_tag: event.currentTarget.value || undefined
          })
        }
        placeholder="Image tag"
        style={{ width: 340 }}
        value={setting.image_tag || ''}
      />
      <ActionIcon
        aria-label="Remove component"
        color="carbon"
        onClick={onRemove}
        style={{ marginTop: 26 }}
        variant="subtle"
      >
        <IconMinusCircle size={18} />
      </ActionIcon>
    </Group>
  )
}

type ConfigTemplateFormItemProps = {
  configTypes: string[]
  customTemplates: CloudconfigTemplate[]
  errors: CloudconfigCreateMetaProfileFormErrors
  index: number
  onChange: (setting: ConfigTemplateSetting) => void
  onRemove: () => void
  selectedConfigTypes: string[]
  setting: ConfigTemplateSetting
}

function ConfigTemplateFormItem({
  configTypes,
  customTemplates,
  errors,
  index,
  onChange,
  onRemove,
  selectedConfigTypes,
  setting
}: ConfigTemplateFormItemProps) {
  const templateOptions = useMemo(
    () =>
      customTemplates
        .filter((template) => template.config_type === setting.key)
        .map((template) => ({
          label: `${template.id}: ${template.name}`,
          value: String(template.id)
        })),
    [customTemplates, setting.key]
  )

  return (
    <Group align="flex-start" gap="sm" wrap="nowrap">
      <Select
        data={getAvailableOptionData(
          configTypes,
          selectedConfigTypes,
          setting.key
        )}
        error={errors[`config_custom_templates.${index}.key`]}
        label="Config type"
        onChange={(nextConfigType) =>
          onChange({
            key: nextConfigType || undefined,
            value: undefined
          })
        }
        placeholder="Select config type"
        required
        searchable
        style={{ width: 200 }}
        value={setting.key || null}
      />
      <Select
        data={templateOptions}
        disabled={!setting.key}
        error={errors[`config_custom_templates.${index}.value`]}
        label="Template"
        onChange={(nextTemplateId) =>
          onChange({
            ...setting,
            value: nextTemplateId ? Number(nextTemplateId) : undefined
          })
        }
        placeholder={
          setting.key ? 'Select template' : 'Select config type first'
        }
        required
        searchable
        style={{ width: 560 }}
        value={setting.value ? String(setting.value) : null}
      />
      <ActionIcon
        aria-label="Remove config template"
        color="carbon"
        onClick={onRemove}
        style={{ marginTop: 26 }}
        variant="subtle"
      >
        <IconMinusCircle size={18} />
      </ActionIcon>
    </Group>
  )
}

export default function CreateMetaProfile({
  clusterType,
  errors = {},
  onChange,
  tenantId,
  value
}: {
  clusterType: CloudconfigClusterType
  errors?: CloudconfigCreateMetaProfileFormErrors
  onChange: (value: CloudconfigCreateMetaProfileForm) => void
  tenantId: string
  value: CloudconfigCreateMetaProfileForm
}) {
  const [componentNames, setComponentNames] = React.useState<string[]>([])
  const [customTemplates, setCustomTemplates] = React.useState<
    CloudconfigTemplate[]
  >([])

  useEffect(() => {
    let mounted = true
    ;(async () => {
      const [componentsResp, templatesResp] = await Promise.all([
        cloudConfigControllerListComponents({ cluster_type: clusterType }),
        cloudConfigControllerListTemplates({
          cluster_type: clusterType,
          tenant_id: tenantId,
          template_type: 'tenant_default'
        })
      ])

      if (mounted) {
        setComponentNames(componentsResp.data.components || [])
        setCustomTemplates(templatesResp.data.items?.map((t) => t) || [])
      }
    })().catch((e) => {
      console.error('fetch meta profile form options failed', e)
      if (mounted) {
        setComponentNames([])
        setCustomTemplates([])
      }
    })

    return () => {
      mounted = false
    }
  }, [clusterType, tenantId])

  const componentSettings = value.component_settings || []
  const configTemplateSettings = value.config_custom_templates || []

  const selectedComponentNames = useMemo(
    () => getSelectedKeys(componentSettings),
    [componentSettings]
  )

  const configTypes = useMemo(
    () =>
      Array.from(
        new Set(
          customTemplates
            .map((template) => template.config_type)
            .filter(Boolean)
        )
      ).sort() as string[],
    [customTemplates]
  )

  const selectedConfigTypes = useMemo(
    () => getSelectedKeys(configTemplateSettings),
    [configTemplateSettings]
  )

  const updateComponentSetting = (index: number, setting: ComponentSetting) => {
    const nextSettings = [...componentSettings]
    nextSettings[index] = setting
    onChange({
      ...value,
      component_settings: nextSettings
    })
  }

  const updateConfigTemplateSetting = (
    index: number,
    setting: ConfigTemplateSetting
  ) => {
    const nextSettings = [...configTemplateSettings]
    nextSettings[index] = setting
    onChange({
      ...value,
      config_custom_templates: nextSettings
    })
  }

  const canAddComponent =
    componentNames.length > selectedComponentNames.length &&
    componentSettings.every((setting) => !!setting.key)

  const canAddConfigTemplate =
    configTypes.length > selectedConfigTypes.length &&
    configTemplateSettings.every((setting) => !!setting.key)

  return (
    <Stack gap="lg">
      <SectionTitle>Meta Profile Basic Information</SectionTitle>
      <TextInput
        error={errors.profile_name}
        label="Profile name"
        onChange={(event) =>
          onChange({
            ...value,
            profile_name: event.currentTarget.value
          })
        }
        required
        value={value.profile_name}
      />

      <Textarea
        label="Description"
        minRows={3}
        onChange={(event) =>
          onChange({
            ...value,
            description: event.currentTarget.value || undefined
          })
        }
        value={value.description || ''}
      />

      <Alert color="yellow" variant="light">
        Notice: Unmentioned components and custom templates use the
        GLOBAL/TENANT default settings.
      </Alert>

      <SectionTitle>Components</SectionTitle>
      <Stack gap="xs">
        {componentSettings.map((setting, index) => (
          <ComponentSettingFormItem
            clusterType={clusterType}
            componentNames={componentNames}
            errors={errors}
            index={index}
            key={index}
            onChange={(nextSetting) =>
              updateComponentSetting(index, nextSetting)
            }
            onRemove={() =>
              onChange({
                ...value,
                component_settings: componentSettings.filter(
                  (_, itemIndex) => itemIndex !== index
                )
              })
            }
            selectedComponentNames={selectedComponentNames}
            setting={setting}
            tenantId={tenantId}
          />
        ))}
        <Button
          disabled={!canAddComponent}
          fullWidth
          leftSection={<IconPlus size={16} />}
          onClick={() =>
            onChange({
              ...value,
              component_settings: [...componentSettings, {}]
            })
          }
          variant="default"
        >
          Add component
        </Button>
      </Stack>

      <SectionTitle>Config Custom Templates</SectionTitle>
      <Stack gap="xs">
        {configTemplateSettings.map((setting, index) => (
          <ConfigTemplateFormItem
            configTypes={configTypes}
            customTemplates={customTemplates}
            errors={errors}
            index={index}
            key={index}
            onChange={(nextSetting) =>
              updateConfigTemplateSetting(index, nextSetting)
            }
            onRemove={() =>
              onChange({
                ...value,
                config_custom_templates: configTemplateSettings.filter(
                  (_, itemIndex) => itemIndex !== index
                )
              })
            }
            selectedConfigTypes={selectedConfigTypes}
            setting={setting}
          />
        ))}
        <Button
          disabled={!canAddConfigTemplate}
          fullWidth
          leftSection={<IconPlus size={16} />}
          onClick={() =>
            onChange({
              ...value,
              config_custom_templates: [...configTemplateSettings, {}]
            })
          }
          variant="default"
        >
          Add config template
        </Button>
      </Stack>
    </Stack>
  )
}
