import {
  ActionIcon,
  ActionIconProps,
  Loader,
  Tabs,
  CopyButton,
  Box,
  Tooltip,
  useMantineTheme,
  Flex,
  Typography,
  HoverCard,
  Button
} from '@tidbcloud/uikit'
import { createStyles } from '@tidbcloud/uikit/emotion'
import {
  IconCopy01,
  IconLayoutBottom,
  IconLayoutBottomFill,
  IconLayoutLeft,
  IconLayoutLeftFill,
  IconLayoutRight,
  IconLayoutRightFill,
  IconInfoCircle
} from '@tidbcloud/uikit/icons'
import { rgba } from '@tidbcloud/uikit/utils'
import clsx from 'clsx'
import { ResizeCallback } from 're-resizable'
import React, { useState, useEffect, useMemo } from 'react'
import { JSONTree, KeyPath } from 'react-json-tree'

import ResizableContainer from 'dbaas/components/ResizableContainer'
import { useHelpCenterContext } from 'dbaas/screens/HelpCenter/context'
import Results from 'dbaas/screens/SqlEditor/MessageLogs/Results'
import { RunningTips, RunErrTips, RunWarningTips, RunSuccTips } from 'dbaas/screens/SqlEditor/MessageLogs/RunningTips'
import { RequestStatus } from 'dbaas/screens/SqlEditor/types'
import { eventTracking } from 'dbaas/utils/tracking'

import { BOTTOM_PANEL_DEFAULT_HEIGHT, BOTTOM_PANEL_MIN_HEIGHT, useDataApiContext } from '../context'
import { useCommonStyles } from '../index.styles'
import { SqlRes } from '../types'

import EmptyTips from './EmptyTips'

interface LayoutIconProps extends ActionIconProps {
  onClick: () => void
}

const EDITOR_PADDING = 16
const useStyles = createStyles((theme, _params, _getRef) => {
  return {
    resTable: {
      overflowX: 'auto',
      '&::-webkit-scrollbar': {
        width: 0,
        '&:horizontal': {
          height: 0
        }
      }
    },
    errInfo: {
      position: 'absolute',
      top: 40,
      left: 0,
      right: 0,
      bottom: 0,
      display: 'flex',
      flexDirection: 'column',
      justifyContent: 'center',
      alignItems: 'center',
      errContent: {
        textAlign: 'center',
        fontSize: 14,
        lineHeight: '24px',
        color: '#e65c5c',
        maxWidth: '60%'
      }
    },
    myTreeClassName: {
      padding: '16px !important',
      position: 'absolute',
      top: 40,
      left: 16,
      right: 16,
      bottom: 0,
      overflowY: 'auto',
      div: {
        '*': {
          color: `${theme.colors.carbon[8]} !important`
        }
      }
    }
  }
})

const LayoutIcon: React.FC<React.PropsWithChildren<LayoutIconProps>> = ({ children, ...props }) => {
  return (
    <ActionIcon
      variant="transparent"
      w={24}
      h={24}
      sx={(theme) => {
        return {
          color: theme.colors.carbon[8],
          ':focus': {
            outline: 'none'
          },
          ':hover': {
            backgroundColor: theme.colors.carbon[2]
          }
        }
      }}
      {...props}
    >
      {children}
    </ActionIcon>
  )
}

const MessageLogs: React.FC<
  React.PropsWithChildren<{ sqlRef: React.RefObject<HTMLDivElement>; maxHeight: number; isRun: boolean }>
> = ({ maxHeight, isRun }) => {
  const runHeight = 340
  const defaultHeight = BOTTOM_PANEL_DEFAULT_HEIGHT
  const minHeight = BOTTOM_PANEL_MIN_HEIGHT
  const headerH = 40
  const [resizing, setResizing] = useState(false)
  const {
    sqlResultList,
    layout: { leftPanel, mainPanel, bottomPanel, rightPanel },
    setLayout,
    resetLeftPanelLayout,
    setLeftPanelLayout,
    setBottomPanelLayout,
    setRightPanelLayout,
    resetRightPanelLayout,
    testRes
  } = useDataApiContext()
  const [activeIndex, setActieIndex] = useState('0')
  const theme = useMantineTheme()
  const { classes } = useStyles()
  const { classes: commanClasses } = useCommonStyles()
  const { dataServiceGuideStep } = useHelpCenterContext()

  useEffect(() => {
    if (testRes) {
      setActieIndex('1')
    }
  }, [testRes])

  useEffect(() => {
    setActieIndex('0')
  }, [sqlResultList])

  useEffect(() => {
    if (isRun && (bottomPanel.hidden || (bottomPanel.height || 0) < runHeight)) {
      setBottomPanelLayout({
        height: runHeight,
        hidden: false
      })
    }
  }, [isRun, bottomPanel.height])

  const messlogResize = () => {
    setResizing(true)
  }

  const onResizeStop: ResizeCallback = (_event, _direction, _elementRef, delta) => {
    setResizing(false)
    const height = (bottomPanel.height || 0) + delta.height
    if (height <= minHeight + 60) {
      setBottomPanelLayout({
        hidden: true
      })
      return
    }

    if (height < maxHeight - EDITOR_PADDING * 4) {
      setLayout({
        leftPanel: { ...leftPanel },
        mainPanel: {
          hidden: false
        },
        bottomPanel: {
          height: height
        },
        rightPanel: { ...rightPanel }
      })
      return
    }
    if (height > maxHeight - EDITOR_PADDING * 4) {
      setLayout({
        leftPanel: { ...leftPanel },
        mainPanel: {
          hidden: true
        },
        bottomPanel: {
          height: maxHeight
        },
        rightPanel: { ...rightPanel }
      })
      return
    }
    setBottomPanelLayout({
      height,
      hidden: false
    })
  }

  const cur = (sqlResultList || {}) as any as SqlRes
  const {
    columns = [],
    rows = [],
    query,
    err_message,
    err_code,
    row_count,
    limit = 0,
    databases,
    start_ms,
    end_ms,
    args
  } = cur.res || {}
  const isError = useMemo(() => {
    return (err_code && err_code !== 200 && cur.status === RequestStatus.Success) || cur.status === RequestStatus.Error
  }, [cur.status, err_code])

  const runArgs = useMemo(() => {
    return args ? args.flat() : []
  }, [args])

  const size = useMemo(() => {
    if (dataServiceGuideStep > 1) {
      return 300
    } else if (bottomPanel.hidden) {
      return minHeight
    } else {
      return bottomPanel.height || defaultHeight
    }
  }, [dataServiceGuideStep, bottomPanel.height])

  useEffect(() => {
    if (!testRes) {
      setActieIndex('0')
    }
  }, [testRes])

  return (
    <ResizableContainer
      data-guide="result"
      as="div"
      className={clsx('sql-editor-container', resizing ? '' : commanClasses.resizerContainer)}
      size={{ width: '100%', height: size }}
      enable={{ top: true }}
      minHeight={minHeight}
      maxHeight={maxHeight}
      onResize={messlogResize}
      onResizeStop={onResizeStop}
      handleClasses={{ top: commanClasses.resizer }}
    >
      <Box
        sx={(theme) => ({
          borderTop: mainPanel.hidden ? 'none' : `1px solid ${theme.colors.carbon[3]}`,
          height: '100%',
          position: 'relative'
        })}
      >
        <Tabs
          value={activeIndex}
          styles={() => ({
            tab: {
              marginRight: 4
            },
            list: {
              marginLeft: 16,
              marginBottom: 2,
              '&::before': {
                'border-style': 'none'
              }
            }
          })}
          onChange={(val: string) => {
            eventTracking('DataService message tab changed', {
              tabName: val
            })
            setActieIndex(val)
          }}
        >
          <Tabs.List>
            <Tabs.Tab value="0">Result</Tabs.Tab>
            {testRes && (
              <Tabs.Tab value="1">
                <Flex align="center">
                  <Typography mr={4}>HTTP Response</Typography>
                  <HoverCard withArrow position="top" shadow="md">
                    <HoverCard.Target>
                      <Box component={IconInfoCircle} size={14} c="carbon.8" />
                    </HoverCard.Target>
                    <HoverCard.Dropdown>
                      <Typography variant="body-sm">
                        Refer to{' '}
                        <a
                          target="_blank"
                          rel="noreferrer"
                          href="https://docs.pingcap.com/tidbcloud/data-service-response-and-status-code"
                        >
                          response schema
                        </a>
                      </Typography>
                    </HoverCard.Dropdown>
                  </HoverCard>
                </Flex>
              </Tabs.Tab>
            )}
          </Tabs.List>

          <Tabs.Panel value="0">
            <Box className={classes.resTable}>
              {!sqlResultList && <EmptyTips />}

              {!!(cur.status !== RequestStatus.Error && columns && columns.length) && (
                <Results
                  columns={columns || []}
                  list={rows || []}
                  message={''}
                  height={bottomPanel.height ? Math.ceil(bottomPanel.height - headerH) : 0}
                  showTips={bottomPanel.hidden ? false : !!sqlResultList}
                  tips={
                    <Flex align="center" justify="space-between">
                      <HoverCard width={280} shadow="md" position="top-start">
                        <HoverCard.Target>
                          <Button variant="subtle" size="xs" ml={-16}>
                            SQL Execution Summary
                          </Button>
                        </HoverCard.Target>
                        <HoverCard.Dropdown>
                          <Typography variant="body-sm">
                            {row_count !== undefined && (
                              <Typography>Response Rows: {Math.min(row_count, limit)}</Typography>
                            )}
                            {end_ms && start_ms && (
                              <Typography mt={8}>
                                Execution Time: {((end_ms ?? 0) - (start_ms ?? 0)) / 1000}s
                              </Typography>
                            )}
                            {!!runArgs.length && (
                              <Flex wrap="wrap" align="center" mt={8}>
                                <Typography mr={4}>Test Values:</Typography>
                                <>
                                  {runArgs.map((arg, idx) => (
                                    <Flex align="center" wrap="nowrap" sx={{ flexGrow: 0, flexShrink: 0 }}>
                                      {arg.name}[<Typography variant="label-sm">{arg.value}</Typography>]
                                      {idx === runArgs.length - 1 ? '' : ','}
                                    </Flex>
                                  ))}
                                </>
                              </Flex>
                            )}
                          </Typography>
                        </HoverCard.Dropdown>
                      </HoverCard>
                      <Box>
                        {row_count !== undefined && (
                          <>
                            showing
                            {row_count > limit ? ` ${limit} rows out of ${row_count}` : ` ${row_count} rows`}
                          </>
                        )}
                        {databases ? ` from database ${databases.join(', ')}` : ''}
                      </Box>
                    </Flex>
                  }
                />
              )}

              {!!cur &&
                cur.status === RequestStatus.Success &&
                !isError &&
                !columns?.length &&
                !cur.res?.warn_count && (
                  <Box p={24}>
                    <RunSuccTips message={query} />
                  </Box>
                )}

              {isError && (
                <Box p={24}>
                  <RunErrTips message={cur.status === RequestStatus.Success ? err_message : cur.res} />
                </Box>
              )}

              {!!cur.res?.warn_count && !columns?.length && (
                <Box p={24}>
                  <RunWarningTips message={cur.res.warn_messages} />
                </Box>
              )}

              {cur.status === RequestStatus.Loading && (
                <Box px={24} pt={24}>
                  <RunningTips />
                </Box>
              )}
            </Box>
          </Tabs.Panel>
          {testRes && (
            <Tabs.Panel value="1">
              {testRes?.loading && (
                <Box className={classes.resTable}>
                  <Box className={classes.errInfo}>
                    <Loader size="sm" />
                    <Typography variant="title-lg" color="carbon.8" mt={16}>
                      Waiting for response
                    </Typography>
                    <Typography variant="body-lg" color="carbon.7">
                      Your endpoint is under testing...
                    </Typography>
                  </Box>
                </Box>
              )}
              {testRes?.error && (
                <Box className={classes.resTable}>
                  <Box className={classes.errInfo}>
                    <Box className="err-content">{testRes.error}</Box>
                  </Box>
                </Box>
              )}

              {!testRes?.error && !testRes?.loading && (
                <Box data-guide="result-json">
                  <JSONTree
                    data-guide="result-json"
                    data={testRes}
                    invertTheme={true}
                    labelRenderer={(keyPath: KeyPath) => <Box c="yellow.8">{keyPath[0]}:</Box>}
                    valueRenderer={(_valueAsString: unknown, value: string) => (
                      <Typography component="span" c="blue">{`${value === '' ? '""' : value}`}</Typography>
                    )}
                    shouldExpandNodeInitially={() => true}
                    theme={{
                      tree: ({ style }) => {
                        return {
                          style: {
                            ...style,
                            backgroundColor: rgba(theme.colors.carbon[6], 0.1),
                            borderRadius: 5,
                            border: `1px solid ${theme.colors.carbon[5]}`,
                            padding: '6px 0'
                          },
                          className: classes.myTreeClassName
                        }
                      }
                    }}
                  />
                  <Box
                    sx={() => ({
                      position: 'absolute',
                      right: 36,
                      top: 60,
                      zIndex: 99
                    })}
                  >
                    <CopyButton value={JSON.stringify(testRes, null, 2)} timeout={2000}>
                      {({ copied, copy }) => (
                        <Tooltip label={copied ? 'Copied' : 'Copy'} withArrow>
                          <ActionIcon color="carbon.8" onClick={copy}>
                            <IconCopy01 size={16} />
                          </ActionIcon>
                        </Tooltip>
                      )}
                    </CopyButton>
                  </Box>
                </Box>
              )}
            </Tabs.Panel>
          )}
        </Tabs>

        <Box
          sx={{
            position: 'absolute',
            right: 16,
            top: 4
          }}
        >
          <Flex align="center">
            <LayoutIcon
              onClick={() => {
                eventTracking(`Dataservice ${leftPanel.hidden ? 'Show' : 'Hide'} SideMenu`)
                if (leftPanel.hidden) {
                  resetLeftPanelLayout()
                } else {
                  setLeftPanelLayout({
                    hidden: true
                  })
                }
              }}
            >
              <Box component={leftPanel.hidden ? IconLayoutLeft : IconLayoutLeftFill} size={16} />
            </LayoutIcon>

            <LayoutIcon
              onClick={() => {
                eventTracking(`DataService ${bottomPanel.hidden ? 'Show' : 'Hide'} Result Panel`)
                if (bottomPanel.hidden) {
                  setLayout({
                    leftPanel: { ...leftPanel },
                    bottomPanel: {
                      ...bottomPanel,
                      height: defaultHeight,
                      hidden: false
                    },
                    mainPanel: {
                      hidden: false
                    },
                    rightPanel: { ...rightPanel }
                  })
                } else {
                  setLayout({
                    leftPanel: { ...leftPanel },
                    bottomPanel: {
                      hidden: true
                    },
                    mainPanel: {
                      hidden: false
                    },
                    rightPanel: { ...rightPanel }
                  })
                }
              }}
            >
              <Box component={bottomPanel.hidden ? IconLayoutBottom : IconLayoutBottomFill} size={16} />
            </LayoutIcon>

            <LayoutIcon
              onClick={() => {
                eventTracking(`DataService ${rightPanel.hidden ? 'Show' : 'Hide'} Right Panel`)
                if (rightPanel.hidden) {
                  resetRightPanelLayout()
                } else {
                  setRightPanelLayout({
                    hidden: true
                  })
                }
              }}
            >
              <Box component={rightPanel.hidden ? IconLayoutRight : IconLayoutRightFill} size={16} />
            </LayoutIcon>
          </Flex>
        </Box>
      </Box>
    </ResizableContainer>
  )
}

export default MessageLogs
