import {
  Accordion,
  ActionIcon,
  Center,
  Divider,
  Group,
  Input,
  Loader,
  notifier,
  Stack,
  Typography
} from '@tidbcloud/uikit'
import { MRT_ColumnDef, ProTable } from '@tidbcloud/uikit/biz'
import { useToggle } from '@tidbcloud/uikit/hooks'
import { IconCheck, IconEdit03, IconXClose } from '@tidbcloud/uikit/icons'
import { groupBy, subtract } from 'lodash-es'
import { useState } from 'react'

import { SharedCluster } from 'dbaas/features/ClusterManagement/cluster'
import { renderNodes } from 'dbaas/features/ClusterManagement/common'
import { queryClient } from 'dbaas/reactQueryClient'
import { ProviderConst } from 'dbaas/screens/ConsoleV2/features/Network/CIDR/helpers'
import { V1beta1ClusterTidbNodeSetting } from 'dbaas/services/api-clients/v2023/cluster-management/v1beta1/interface'
import {
  getClusterServiceListNodeInstancesQueryKey,
  useTidbNodeGroupServiceUpdateTidbNodeGroup
} from 'dbaas/services/api-clients/v2023/dedicated/v1beta1'
import {
  Dedicatedv1beta1ComponentType,
  V1beta1NodeInstance
} from 'dbaas/services/api-clients/v2023/dedicated/v1beta1/interface'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import useStores from 'dbaas/stores/useStores'

import { ResourceCard, ResourceStatus } from './comp/ResourceStatus'
import { ResourceSummary } from './comp/ResourceSummary'
import { pageSize, tableStyle } from './const'
import { renderGroups, renderSpec, renderSpecVersion, useAccordionStyle, useFilterInstanceData } from './helper'
import { NodeInstanceStatus, NodeMapLayout } from './NodeMap'

const GroupName = ({
  name,
  nodesCount,
  clusterId,
  tidbNodeGroupId,
  isDefaultGroup
}: {
  name: string
  nodesCount: number
  clusterId: string
  tidbNodeGroupId: string
  isDefaultGroup: boolean
}) => {
  const [groupName, setGroupName] = useState(name)

  const [editMode, toggleEditMode] = useToggle([false, true])

  const [instanceListIsLoading, setInstanceListIsLoading] = useState(false)

  const {
    store: {
      lastVisitLocation: { orgId, projectId }
    }
  } = useStores()

  const { mutateAsync, isLoading } = useTidbNodeGroupServiceUpdateTidbNodeGroup({
    mutation: {
      onSuccess: async () => {
        setInstanceListIsLoading(true)
        await queryClient.invalidateQueries(getClusterServiceListNodeInstancesQueryKey(clusterId, { orgId, projectId }))
        setInstanceListIsLoading(false)
        handleExitEditMode()
      },
      onError: (error) => {
        const errMsg = getErrorMessage(error)
        notifier.error(errMsg)
      }
    }
  })

  const handleChange = () => {
    mutateAsync({
      clusterId,
      tidbNodeGroupId,
      data: { displayName: groupName },
      params: {
        orgId,
        projectId
      }
    })
  }

  const handleExitEditMode = () => {
    setGroupName(name)
    toggleEditMode()
  }

  const modifyNameLoading = isLoading || instanceListIsLoading

  if (editMode) {
    return (
      <Group style={{ position: 'relative' }}>
        <Input
          w={400}
          defaultValue={groupName}
          rightSection={modifyNameLoading ? <Loader size="sm" /> : void 0}
          onChange={(e) => setGroupName(e.target.value)}
        />
        {!modifyNameLoading && (
          <>
            <ActionIcon onClick={handleExitEditMode}>
              <IconXClose strokeWidth={2} size={20} />
            </ActionIcon>
            <ActionIcon c="blue.7" onClick={handleChange}>
              <IconCheck strokeWidth={2} size={20} />
            </ActionIcon>
          </>
        )}
      </Group>
    )
  } else {
    return (
      <Group>
        <Typography variant="action-md" c="gray.8">
          {name}
        </Typography>
        <Typography variant="label-sm" c="gray.6">
          ({renderNodes(nodesCount)})
        </Typography>
        {!isDefaultGroup && (
          <ActionIcon onClick={() => toggleEditMode()}>
            <IconEdit03 strokeWidth={2} size={16} />
          </ActionIcon>
        )}
      </Group>
    )
  }
}

export const TiDBMap = ({
  layout,
  status,
  modifying,
  cluster,
  data,
  nodeSetting,
  specVersionEnabled
}: {
  layout: NodeMapLayout
  status: NodeInstanceStatus
  modifying: boolean
  cluster: SharedCluster
  data: V1beta1NodeInstance[]
  nodeSetting?: V1beta1ClusterTidbNodeSetting
  specVersionEnabled?: boolean
}) => {
  const filteredData = useFilterInstanceData(data, status)

  const [page, setPage] = useState(0)

  let nodes
  if (filteredData.length === 0) {
    nodes = <Center>No nodes.</Center>
  } else {
    if (cluster.provider === ProviderConst.AWS || cluster.provider === ProviderConst.GCP) {
      const groups = groupBy(filteredData, (v) => v.tidbNodeGroupDisplayName)
      const sortedGroups = Object.entries(groups).sort((a, b) => {
        const aValue = a[1][0]
        const bValue = b[1][0]
        if (aValue.isDefaultTidbNodeGroup === true) {
          return -1
        } else if (bValue.isDefaultTidbNodeGroup === true) {
          return 1
        } else {
          return subtract(+(aValue.tidbNodeGroupId ?? 0), +(bValue?.tidbNodeGroupId ?? 0))
        }
      })
      nodes = sortedGroups.map(([key, value], idx, groupArr) => (
        <Stack gap={24} key={key}>
          <GroupName
            name={key}
            nodesCount={value.length}
            clusterId={cluster.id}
            tidbNodeGroupId={value[0].tidbNodeGroupId ?? ''}
            isDefaultGroup={value[0].isDefaultTidbNodeGroup ?? false}
          />
          <Group>
            {value.map((v) => (
              <ResourceCard
                key={v.instanceId}
                name={v.instanceId ?? ''}
                state={v.state}
                availabilityZone={v.availabilityZone ?? ''}
              />
            ))}
          </Group>
          {idx !== groupArr.length - 1 && <Divider mb={24} />}
        </Stack>
      ))
    } else {
      nodes = (
        <Group>
          {filteredData.map((v) => (
            <ResourceCard
              key={v.instanceId}
              name={v.instanceId ?? ''}
              state={v.state}
              availabilityZone={v.availabilityZone ?? ''}
            />
          ))}
        </Group>
      )
    }
  }

  const columns: MRT_ColumnDef<V1beta1NodeInstance>[] = [
    {
      header: 'Name',
      accessorKey: 'instanceId'
    },
    {
      header: 'Node Group Name',
      accessorKey: 'tidbNodeGroupDisplayName'
    },
    {
      header: 'Status',
      accessorFn: (row) => <ResourceStatus status={row.state} />
    },
    {
      header: 'Available Zone',
      accessorFn: (row) => `AZ: ${row.availabilityZone}`
    },
    {
      header: 'vCPU, RAM',
      size: 300,
      accessorFn: (row) =>
        row.componentType === Dedicatedv1beta1ComponentType.TIPROXY
          ? row.nodeSpecDisplayName
          : renderSpec(row.vCpu ?? 0, row.memorySizeGi ?? 0) +
            renderSpecVersion(row.nodeSpecVersion, specVersionEnabled)
    }
  ]

  const groupAmount = nodeSetting?.tidbNodeGroups.length ?? 0

  const showPagination = filteredData.length > pageSize

  const accordionStyle = useAccordionStyle(layout, showPagination)

  return (
    <Accordion defaultValue="TiDB" variant="contained" styles={accordionStyle}>
      <Accordion.Item value="TiDB">
        <Accordion.Control>
          <ResourceSummary
            title="TiDB"
            desc="The computing node that aggregates data from queries returned from transactional or analytical stores."
            modifying={modifying}
            data={filteredData}
            spec={
              <Typography variant="label-lg">
                {nodeSetting?.nodeSpecDisplayName} , {renderGroups(groupAmount)}
              </Typography>
            }
          />
        </Accordion.Control>
        <Accordion.Panel>
          {layout === 'block' && nodes}
          {layout === 'table' && (
            <ProTable
              {...tableStyle}
              columns={columns}
              emptyMessage="No Data"
              data={filteredData}
              rowCount={showPagination ? filteredData.length : undefined}
              state={{ pagination: { pageIndex: page, pageSize } }}
              pagination={{ onChange: (page) => setPage(page - 1) }}
            ></ProTable>
          )}
        </Accordion.Panel>
      </Accordion.Item>
    </Accordion>
  )
}
