import { Box, Group, Stack, Typography } from '@tidbcloud/uikit'
import { MRT_ColumnDef, ProTable } from '@tidbcloud/uikit/biz'

import {
  CloudconfigChangedParameter,
  CloudconfigConfigurationChange,
  CloudconfigConfigurationComponentChange,
  CloudconfigConfigurationFileChange,
  CloudconfigConfigurationVersionChange
} from 'apiClient/services/devops/interface'
import CodeValue from 'components/CodeValue'
import ConfigName from 'components/ConfigName'

type ComponentChangeRow = {
  component?: string
  key: string
  type: string
  from?: string
  to?: string
}

type ModifiedTable = {
  param?: CloudconfigChangedParameter
  filename?: string
}

type ChangedImageTag = NonNullable<
  CloudconfigConfigurationComponentChange['modified_image_tag']
>
type ChangedVersion = NonNullable<
  CloudconfigConfigurationComponentChange['modified_version']
>

const fileColumnHeaders = [
  'Filename',
  'Name',
  'Source',
  'Previous Value',
  'New Value'
]
const fileColumnSizes = [90, 210, 150, 180, 180]

const flatFiles = (
  files: CloudconfigConfigurationFileChange[] | null | undefined
) => {
  const rows: ModifiedTable[] = []

  files?.forEach((file) => {
    file.modified_parameters?.forEach((param) => {
      rows.push({ filename: file.filename, param })
    })
  })

  return rows
}

const convertToString = (input: unknown): string => {
  if (input === null) {
    return '-'
  }

  if (
    typeof input === 'string' ||
    typeof input === 'number' ||
    typeof input === 'boolean'
  ) {
    return input.toString()
  }

  return JSON.stringify(input)
}

const displayValue = (value: unknown) => convertToString(value ?? null)

const hasVersionChange = (
  version?: CloudconfigConfigurationVersionChange | null
) => {
  return (
    version != null && (version.from !== undefined || version.to !== undefined)
  )
}

const hasImageTagChange = (imageTag?: ChangedImageTag | null) => {
  return (
    imageTag != null &&
    (imageTag.from !== undefined || imageTag.to !== undefined)
  )
}

const hasComponentVersionChange = (version?: ChangedVersion | null) => {
  return (
    version != null && (version.from !== undefined || version.to !== undefined)
  )
}

const componentChangeRows = (
  components?: CloudconfigConfigurationComponentChange[] | null
) => {
  const rows: ComponentChangeRow[] = []

  components?.forEach((component) => {
    if (hasComponentVersionChange(component.modified_version)) {
      rows.push({
        component: component.component,
        key: `${component.id || component.component}-version`,
        type: 'Version',
        from: component.modified_version?.from,
        to: component.modified_version?.to
      })
    }
    if (hasImageTagChange(component.modified_image_tag)) {
      rows.push({
        component: component.component,
        key: `${component.id || component.component}-image-tag`,
        type: 'Image Tag',
        from: component.modified_image_tag?.from,
        to: component.modified_image_tag?.to
      })
    }
  })

  return rows
}

const compactTextStyle = {
  fontSize: 12,
  lineHeight: '18px',
  overflowWrap: 'anywhere',
  whiteSpace: 'normal',
  wordBreak: 'break-word'
} as const

const sectionStyle = {
  background: 'var(--mantine-color-carbon-0, #f8fafc)',
  border: '1px solid var(--mantine-color-carbon-2, #e9ecef)',
  borderRadius: 6,
  padding: '10px 12px'
} as const

const versionValueStyle = {
  alignItems: 'center',
  display: 'flex',
  gap: 8,
  minWidth: 220
} as const

const tableProps = {
  mantinePaperProps: {
    shadow: 'none',
    style: {
      background: 'transparent'
    }
  },
  mantineTableProps: {
    sx: {
      tableLayout: 'fixed' as const,
      width: '100%'
    }
  },
  mantineTableHeadCellProps: {
    sx: {
      backgroundColor: 'var(--mantine-color-carbon-1, #f1f3f5)',
      color: 'var(--mantine-color-carbon-7, #495057)',
      fontSize: 12,
      fontWeight: 600,
      lineHeight: '18px',
      padding: '6px 10px !important',
      whiteSpace: 'normal'
    }
  },
  mantineTableBodyCellProps: {
    sx: {
      fontSize: 12,
      lineHeight: '18px',
      overflow: 'visible',
      padding: '6px 10px !important',
      whiteSpace: 'normal'
    }
  }
}

const getEmptyValue = () => (
  <Typography variant="body-sm" c="carbon.5" style={compactTextStyle}>
    -
  </Typography>
)

const getTextValue = (value: unknown) => {
  const text = displayValue(value)

  if (text === '-') {
    return getEmptyValue()
  }

  return (
    <Typography variant="body-sm" style={compactTextStyle}>
      {text}
    </Typography>
  )
}

const getCodeValue = (value: unknown) => {
  return <CodeValue value={value} />
}

const componentColumns: MRT_ColumnDef<ComponentChangeRow>[] = [
  {
    header: 'Component',
    accessorKey: 'component',
    size: 130,
    Cell: ({ row }) => getTextValue(row.original.component)
  },
  {
    header: 'Name',
    accessorKey: 'type',
    size: 140,
    Cell: ({ row }) => getTextValue(row.original.type)
  },
  {
    header: 'Previous Value',
    accessorKey: 'from',
    size: 240,
    Cell: ({ row }) => getCodeValue(row.original.from)
  },
  {
    header: 'New Value',
    accessorKey: 'to',
    size: 240,
    Cell: ({ row }) => getCodeValue(row.original.to)
  }
]

const fileColumns: MRT_ColumnDef<ModifiedTable>[] = fileColumnHeaders.map(
  (header, index) => ({
    header,
    id: `file-change-${index}`,
    size: fileColumnSizes[index],
    Cell: ({ row }) => {
      const record = row.original

      switch (index) {
        case 0:
          return getTextValue(record.filename)
        case 1:
          return <ConfigName value={record.param?.name} />
        case 2:
          return getTextValue(
            `${String(record.param?.from_source)} -> ${String(
              record.param?.to_source
            )}`
          )
        case 3:
          return getCodeValue(record.param?.from)
        case 4:
          return getCodeValue(record.param?.to)
        default:
          return getEmptyValue()
      }
    }
  })
)

const ExpandTable = ({
  change
}: {
  change?: CloudconfigConfigurationChange
}) => {
  const version = change?.scope?.version
  const imageComponents = componentChangeRows(change?.scope?.components)
  const files = flatFiles(change?.scope?.files)
  const showVersion = hasVersionChange(version)
  const mergeVersionIntoComponents =
    showVersion &&
    imageComponents.length > 0 &&
    ['logicalcluster', 'sharedpool'].includes(change?.change_type || '')
  const components = mergeVersionIntoComponents
    ? [
        {
          key: 'version-change',
          type: 'Version',
          from: version?.from,
          to: version?.to
        },
        ...imageComponents
      ]
    : imageComponents
  const showStandaloneVersion = showVersion && !mergeVersionIntoComponents
  const showComponents = components.length > 0
  const showFiles = files.length > 0

  if (!showStandaloneVersion && !showComponents && !showFiles) {
    return (
      <Box py={24}>
        <Typography variant="body-sm" c="carbon.5" ta="center">
          No changes
        </Typography>
      </Box>
    )
  }

  return (
    <Stack gap="xs" py={4}>
      {showStandaloneVersion && (
        <Box style={sectionStyle}>
          <Typography variant="title-sm" mb={8}>
            Version Changes
          </Typography>
          <Group gap="xl" wrap="wrap">
            <Box style={versionValueStyle}>
              <Typography variant="label-sm" c="carbon.6">
                Previous Version
              </Typography>
              {getCodeValue(version?.from)}
            </Box>
            <Box style={versionValueStyle}>
              <Typography variant="label-sm" c="carbon.6">
                New Version
              </Typography>
              {getCodeValue(version?.to)}
            </Box>
          </Group>
        </Box>
      )}
      {showComponents && (
        <Box style={sectionStyle}>
          <Typography variant="title-sm" mb={8}>
            Component Changes
          </Typography>
          <ProTable
            columns={componentColumns}
            data={components}
            emptyMessage="No component changes"
            enablePagination={false}
            withBorder={false}
            {...tableProps}
          />
        </Box>
      )}
      {showFiles && (
        <Box style={sectionStyle}>
          <Typography variant="title-sm" mb={8}>
            Configuration File Changes
          </Typography>
          <ProTable
            columns={fileColumns}
            data={files}
            emptyMessage="No file changes"
            enablePagination={false}
            withBorder={false}
            {...tableProps}
          />
        </Box>
      )}
    </Stack>
  )
}

export default ExpandTable
