import {
  Form,
  Input,
  InputNumber,
  Table,
  Switch,
  Card,
  Spin,
  Modal,
  Checkbox,
  Select,
  Collapse,
  Row,
  Col
} from 'antd'
import { useEffect, useState, useCallback } from 'react'

import {
  getClusterComponents,
  clusterServiceCreateMigrationCluster,
  regionControllerListRegions
} from 'apiClient/services/devops'
import {
  ClusterGetClusterComponentsResp,
  ClusterCreateMigrationClusterRequest
} from 'apiClient/services/devops/interface'
import ModalForm from 'components/ModalForm'

const { Panel } = Collapse

type SourceClusterContext = {
  provider?: string | null
  region?: string | null
  tenant_id?: string | number | null
  deploy_type?: string | null
}

type CreateMigrationClusterConfigSyncRequest = {
  allow_fallback_to_backup?: boolean
  skip_system_variables?: boolean
  skip_pd_config?: boolean
  skip_pd_server_config?: boolean
  skip_placement_rules?: boolean
  skip_store_limits?: boolean
}

type RestoreModeValue = 'SNAPSHOT' | 'PITR'

type CreateMigrationClusterRequestPayload =
  ClusterCreateMigrationClusterRequest & {
    target_region_id?: string
    backup_region_id?: string
    restore_mode?: RestoreModeValue
    config_sync_strategy?: CreateMigrationClusterConfigSyncRequest
  }

export type CreateMigrationClusterFormType =
  CreateMigrationClusterRequestPayload & {
    source_cluster_id: string
  }

type RegionRecord = {
  region?: {
    id?: string
    display_name?: string
    logical_name?: string
    cloud_provider_id?: string
    state?: string
    is_public?: boolean
    opened_tenant_ids?: string[]
    sort_priority?: number
  }
}

type RegionOption = {
  label: string
  value: string
  logicalName?: string
  sortPriority?: number
}

const RESTORE_MODE_OPTIONS: { label: string; value: RestoreModeValue }[] = [
  { label: 'SNAPSHOT', value: 'SNAPSHOT' },
  { label: 'PITR', value: 'PITR' }
]

const CONFIG_SYNC_FIELDS: {
  key: keyof CreateMigrationClusterConfigSyncRequest
  label: string
}[] = [
  {
    key: 'allow_fallback_to_backup',
    label: 'Allow Fallback To Backup'
  },
  {
    key: 'skip_system_variables',
    label: 'Skip System Variables'
  },
  {
    key: 'skip_pd_config',
    label: 'Skip PD Config'
  },
  {
    key: 'skip_pd_server_config',
    label: 'Skip PD Server Config'
  },
  {
    key: 'skip_placement_rules',
    label: 'Skip Placement Rules'
  },
  {
    key: 'skip_store_limits',
    label: 'Skip Store Limits'
  }
]

const REGION_PLACEHOLDER = 'Only regions under the same provider are supported'

function normalizeEnumToken(value?: string | null) {
  return (value || '')
    .trim()
    .toUpperCase()
    .replace(/[^A-Z0-9]+/g, '_')
    .replace(/^_+|_+$/g, '')
}

function isSameRegion(
  sourceRegion?: string | null,
  targetRegion?: string | null
) {
  const source = normalizeEnumToken(sourceRegion)
  const target = normalizeEnumToken(targetRegion)
  return source !== '' && target !== '' && source === target
}

function isSameRegionOption(
  sourceRegion?: string | null,
  region?: RegionRecord['region']
) {
  if (!region) {
    return false
  }

  return (
    isSameRegion(sourceRegion, region.logical_name) ||
    isSameRegion(sourceRegion, region.id)
  )
}

function buildRegionOptionLabel(
  region: RegionRecord['region'],
  sourceRegion?: string | null
) {
  const displayName =
    region?.logical_name ||
    region?.display_name ||
    region?.id ||
    'Unknown Region'
  const label =
    region?.id && region.id !== displayName
      ? `${displayName} (${region.id})`
      : displayName

  return isSameRegionOption(sourceRegion, region) ? `${label} (same)` : label
}

function buildRegionOptions(
  regions: RegionRecord[],
  sourceProvider?: string | null,
  sourceRegion?: string | null,
  tenantId?: string | number | null
) {
  const normalizedProvider = normalizeEnumToken(sourceProvider)
  const normalizedTenantId =
    tenantId === undefined || tenantId === null ? '' : String(tenantId).trim()

  return regions
    .filter((record) => {
      if (!record.region?.id) {
        return false
      }
      if (record.region.state && record.region.state !== 'ACTIVE') {
        return false
      }
      const isPublic = record.region.is_public === true
      const openedTenantIds = record.region.opened_tenant_ids || []
      const openedForTenant =
        normalizedTenantId !== '' &&
        openedTenantIds.some(
          (openedTenantId) => openedTenantId.trim() === normalizedTenantId
        )
      if (!isPublic && !openedForTenant) {
        return false
      }

      if (!normalizedProvider) {
        return true
      }

      return (
        normalizeEnumToken(record.region.cloud_provider_id) ===
        normalizedProvider
      )
    })
    .map((record) => ({
      label: buildRegionOptionLabel(record.region, sourceRegion),
      value: record.region!.id!,
      logicalName: record.region?.logical_name,
      sortPriority: record.region?.sort_priority
    }))
    .sort((left, right) => {
      const leftPriority = left.sortPriority ?? Number.MAX_SAFE_INTEGER
      const rightPriority = right.sortPriority ?? Number.MAX_SAFE_INTEGER

      if (leftPriority !== rightPriority) {
        return leftPriority - rightPriority
      }

      return left.label.localeCompare(right.label)
    })
}

function normalizeCreateMigrationClusterPayload(
  values: CreateMigrationClusterFormType
): CreateMigrationClusterRequestPayload {
  const payload: Record<string, unknown> = { ...values }

  delete payload.source_cluster_id

  Object.entries(payload).forEach(([key, value]) => {
    if (typeof value !== 'string') {
      return
    }

    const trimmed = value.trim()
    if (trimmed === '') {
      delete payload[key]
      return
    }

    payload[key] = trimmed
  })

  const configSyncStrategy = payload.config_sync_strategy as
    | CreateMigrationClusterConfigSyncRequest
    | undefined

  if (configSyncStrategy) {
    const normalizedConfigSyncStrategy: CreateMigrationClusterConfigSyncRequest =
      {
        allow_fallback_to_backup:
          configSyncStrategy.allow_fallback_to_backup === true,
        skip_system_variables:
          configSyncStrategy.skip_system_variables === true,
        skip_pd_config: configSyncStrategy.skip_pd_config === true,
        skip_pd_server_config:
          configSyncStrategy.skip_pd_server_config === true,
        skip_placement_rules: configSyncStrategy.skip_placement_rules === true,
        skip_store_limits: configSyncStrategy.skip_store_limits === true
      }

    if (Object.values(normalizedConfigSyncStrategy).some(Boolean)) {
      payload.config_sync_strategy = normalizedConfigSyncStrategy
    } else {
      delete payload.config_sync_strategy
    }
  }

  return payload as unknown as CreateMigrationClusterRequestPayload
}

export default function CreateMigrationCluster({
  cluster_id,
  cluster,
  open,
  onClose
}: {
  cluster_id: string
  cluster: SourceClusterContext
  open: boolean
  onClose: () => void
}) {
  const [clusterComps, setClusterComps] =
    useState<ClusterGetClusterComponentsResp>()
  const [regionOptions, setRegionOptions] = useState<RegionOption[]>([])
  const [isLoading, setIsLoading] = useState(false)
  const [allowFallbackTouched, setAllowFallbackTouched] = useState(false)
  const [form] = Form.useForm<CreateMigrationClusterFormType>()
  const targetRegionId = Form.useWatch('target_region_id', form)

  const fetch = useCallback(async () => {
    if (!cluster_id) return

    setIsLoading(true)
    setClusterComps(undefined)
    setRegionOptions([])
    setAllowFallbackTouched(false)
    form.resetFields()

    try {
      const clusterComponentsResp = await getClusterComponents(cluster_id)
      const data = clusterComponentsResp.data
      setClusterComps(data)
      try {
        const regionsResp = await regionControllerListRegions({
          deploy_type: cluster.deploy_type || 'DEPLOY_TYPE_DEDICATED',
          tenant_id: cluster.tenant_id ? String(cluster.tenant_id) : undefined
        })
        setRegionOptions(
          buildRegionOptions(
            ((regionsResp as any).data?.regions || []) as RegionRecord[],
            cluster.provider,
            cluster.region,
            cluster.tenant_id
          )
        )
      } catch (regionError) {
        console.error('Failed to fetch migration cluster regions', regionError)
      }

      const initValues: CreateMigrationClusterFormType = {
        source_cluster_id: cluster_id,
        cluster_display_name: '',
        tidb_node_setting: []
      }
      data.tidb!.forEach((comp) => {
        initValues.tidb_node_setting?.push({
          display_name: comp.tidb_group_display_name!,
          node_count: comp.count,
          node_spec_key: comp.spu_name,
          is_default_group: comp.is_default_tidb_group
        })
      })
      data.tikv?.forEach((comp) => {
        initValues.tikv_node_setting = {
          node_count: comp.count,
          node_spec_key: comp.spu_name
        }
      })
      data.tiflash?.forEach((comp) => {
        initValues.tiflash_node_setting = {
          node_count: comp.count,
          node_spec_key: comp.spu_name
        }
      })
      data.pd?.forEach((comp) => {
        initValues.pd_node_setting = {
          node_spec_key: comp.spu_name
        }
      })
      form.setFieldsValue(initValues)
    } catch (error: any) {
      console.error('Failed to fetch migration cluster form data', error)
    } finally {
      setIsLoading(false)
    }
  }, [
    cluster.deploy_type,
    cluster.provider,
    cluster.region,
    cluster.tenant_id,
    cluster_id,
    form
  ])

  useEffect(() => {
    if (open) {
      fetch()
    } else {
      setIsLoading(false)
      setClusterComps(undefined)
      setRegionOptions([])
      setAllowFallbackTouched(false)
      form.resetFields()
    }
  }, [open, fetch, form])

  useEffect(() => {
    if (allowFallbackTouched) {
      return
    }

    const selectedTargetRegion = regionOptions.find(
      (option) => option.value === targetRegionId
    )
    const shouldEnableFallback = Boolean(
      targetRegionId &&
        selectedTargetRegion &&
        !isSameRegionOption(cluster.region, {
          id: selectedTargetRegion.value,
          logical_name: selectedTargetRegion.logicalName
        })
    )
    const currentConfigSyncStrategy =
      form.getFieldValue('config_sync_strategy') || {}

    if (
      currentConfigSyncStrategy.allow_fallback_to_backup ===
      shouldEnableFallback
    ) {
      return
    }

    form.setFieldsValue({
      config_sync_strategy: {
        ...currentConfigSyncStrategy,
        allow_fallback_to_backup: shouldEnableFallback
      }
    })
  }, [
    allowFallbackTouched,
    cluster.region,
    form,
    regionOptions,
    targetRegionId
  ])

  const onSubmit = async (values: CreateMigrationClusterFormType) => {
    const payload = normalizeCreateMigrationClusterPayload(values)

    try {
      var resp = (
        await clusterServiceCreateMigrationCluster(cluster_id, payload)
      ).data.instance_id

      Modal.success({
        title: 'MigrationCluster Created',
        content: (
          <div>
            <p>
              The operation has been created.
              <br></br>
              However, you should go to to the <b>Detail</b> Page to check the
              Approval information. Click <b>OK</b> will redirect you to that
              page.
            </p>
          </div>
        ),
        onOk() {
          window.open(`/operations/${resp}`, '_blank')
        }
      })

      onClose()
    } catch (error) {
      Modal.error({
        title: error.response.data.errcode,
        content: error.response.data.errmsg
      })
    }
  }

  const renderForm = useCallback(() => {
    if (isLoading) {
      return (
        <div
          style={{
            display: 'flex',
            justifyContent: 'center',
            alignItems: 'center',
            minHeight: '300px'
          }}
        >
          <Spin size="large" tip="Loading cluster data..." />
        </div>
      )
    }

    return (
      <>
        <Form.Item label="Source Cluster ID" name="source_cluster_id">
          <Input disabled={true} />
        </Form.Item>
        <Form.Item
          label="Target Cluster Display Name"
          name="cluster_display_name"
          rules={[
            { required: true, message: 'Please input cluster display name' }
          ]}
        >
          <Input />
        </Form.Item>
        <Form.Item label="Target Cluster Project ID" name="target_project_id">
          <Input placeholder="Same as the source cluster by default." />
        </Form.Item>
        <Form.Item label="Target Region ID" name="target_region_id">
          <Select
            allowClear
            showSearch
            optionFilterProp="label"
            placeholder={REGION_PLACEHOLDER}
            options={regionOptions}
          />
        </Form.Item>
        <Form.Item
          label="Create Cluster TiDB Image Tag"
          name="create_tidb_image_tag"
        >
          <Input />
        </Form.Item>
        <Form.Item
          label="Create Cluster TiKV Image Tag"
          name="create_tikv_image_tag"
        >
          <Input />
        </Form.Item>
        <Form.Item
          label="Create Cluster PD Image Tag"
          name="create_pd_image_tag"
        >
          <Input />
        </Form.Item>
        <Form.Item
          label="Create Cluster TiFlash Image Tag"
          name="create_tiflash_image_tag"
        >
          <Input />
        </Form.Item>
        <Form.Item label="Backup ID" name="backup_id">
          <Input />
        </Form.Item>
        <Form.Item label="Backup Region ID" name="backup_region_id">
          <Select
            allowClear
            showSearch
            optionFilterProp="label"
            placeholder={REGION_PLACEHOLDER}
            options={regionOptions}
          />
        </Form.Item>
        <Form.Item label="Restore Mode" name="restore_mode">
          <Select
            allowClear
            placeholder="Select a restore mode"
            options={RESTORE_MODE_OPTIONS}
          />
        </Form.Item>
        <Form.Item label="BR Version" name="br_version">
          <Input />
        </Form.Item>
        <Form.Item label="BR Image Tag" name="br_image_tag">
          <Input />
        </Form.Item>
        <Form.Item label="BR Custom Options" name="br_custom_options">
          <Input placeholder="overwrite BR options, options set via config-center are ignored" />
        </Form.Item>
        <Form.Item label="Target Cluster Version" name="target_cluster_version">
          <Input />
        </Form.Item>
        <Form.Item label="Upgrade Cluster TiDB Image Tag" name="tidb_image_tag">
          <Input />
        </Form.Item>
        <Form.Item label="Upgrade Cluster TiKV Image Tag" name="tikv_image_tag">
          <Input />
        </Form.Item>
        <Form.Item label="Upgrade Cluster PD Image Tag" name="pd_image_tag">
          <Input />
        </Form.Item>
        <Form.Item
          label="Upgrade Cluster TiFlash Image Tag"
          name="tiflash_image_tag"
        >
          <Input />
        </Form.Item>

        <Form.Item
          label="Reason"
          name="reason"
          rules={[{ required: true, message: 'Please input reason' }]}
        >
          <Input.TextArea rows={4} />
        </Form.Item>

        <Card size="small" title="TiDB Node Groups">
          <Form.List name="tidb_node_setting">
            {(fields, { add, remove }) => (
              <>
                <Table
                  dataSource={fields}
                  pagination={false}
                  rowKey="display_name"
                  size="small"
                >
                  <Table.Column
                    title="Display Name"
                    dataIndex="display_name"
                    key="display_name"
                    render={(_, field: any) => (
                      <Form.Item name={[field.name, 'display_name']} noStyle>
                        <Input placeholder="Node Group Name" disabled={true} />
                      </Form.Item>
                    )}
                  />
                  <Table.Column
                    title="Node Count"
                    dataIndex="node_count"
                    key="node_count"
                    width={150}
                    render={(_, field: any) => (
                      <Form.Item name={[field.name, 'node_count']} noStyle>
                        <InputNumber
                          min={0}
                          placeholder="Count"
                          style={{ width: '100%' }}
                        />
                      </Form.Item>
                    )}
                  />
                  <Table.Column
                    title="Node Spec"
                    dataIndex="node_spec_key"
                    key="node_spec_key"
                    render={(_, field: any) => (
                      <Form.Item name={[field.name, 'node_spec_key']} noStyle>
                        <Input placeholder="e.g., 8C16G" />
                      </Form.Item>
                    )}
                  />
                  <Table.Column
                    title="Is Default Group"
                    dataIndex="is_default_group"
                    key="is_default_group"
                    width={100}
                    align="center"
                    render={(_, field: any) => (
                      <Form.Item
                        name={[field.name, 'is_default_group']}
                        valuePropName="checked"
                        noStyle
                      >
                        <Switch disabled={true} />
                      </Form.Item>
                    )}
                  />
                </Table>
              </>
            )}
          </Form.List>
        </Card>
        <br></br>

        <Card size="small" title="TiKV Node Setting">
          <Form.Item
            name={['tikv_node_setting', 'node_count']}
            label="Node Count"
          >
            <InputNumber min={0} style={{ width: '100%' }} />
          </Form.Item>
          <Form.Item
            name={['tikv_node_setting', 'node_spec_key']}
            label="Node Spec"
          >
            <Input />
          </Form.Item>
        </Card>
        <br></br>

        {clusterComps?.tiflash && clusterComps?.tiflash.length > 0 && (
          <Card size="small" title="TiFlash Node Setting">
            <Form.Item
              name={['tiflash_node_setting', 'node_count']}
              label="Node Count"
            >
              <InputNumber min={0} style={{ width: '100%' }} />
            </Form.Item>
            <Form.Item
              name={['tiflash_node_setting', 'node_spec_key']}
              label="Node Spec"
            >
              <Input />
            </Form.Item>
          </Card>
        )}

        <Card size="small" title="PD Node Setting">
          <Form.Item
            name={['pd_node_setting', 'node_spec_key']}
            label="Node Spec"
          >
            <Input />
          </Form.Item>
        </Card>
        <br></br>

        <Collapse style={{ marginTop: 16 }}>
          <Panel header="Config Sync Strategy" key="config-sync-strategy">
            <Row gutter={[16, 8]}>
              {CONFIG_SYNC_FIELDS.map((field) => (
                <Col key={field.key} span={8}>
                  <Form.Item
                    name={['config_sync_strategy', field.key]}
                    valuePropName="checked"
                    style={{ marginBottom: 4 }}
                  >
                    <Checkbox
                      style={{ lineHeight: '20px' }}
                      onChange={() => {
                        if (field.key === 'allow_fallback_to_backup') {
                          setAllowFallbackTouched(true)
                        }
                      }}
                    >
                      {field.label}
                    </Checkbox>
                  </Form.Item>
                </Col>
              ))}
            </Row>
          </Panel>
        </Collapse>
      </>
    )
  }, [isLoading, regionOptions])

  return (
    <div>
      <ModalForm<CreateMigrationClusterFormType>
        title="Create Migration Cluster"
        name="create_migration_cluster"
        visible={open}
        form={form}
        getFields={renderForm}
        onSubmit={onSubmit}
        onCancel={onClose}
        width={1000}
      ></ModalForm>
    </div>
  )
}
