import * as React from "react";
import Box, { BoxProps } from "@mui/material/Box";
import Button from "@mui/material/Button";
import LoadingButton from "@mui/lab/LoadingButton";
import InputLabel from "@mui/material/InputLabel";
import MenuItem from "@mui/material/MenuItem";
import FormControl from "@mui/material/FormControl";
import Select, { SelectChangeEvent, SelectProps } from "@mui/material/Select";
import Grid from "@mui/material/Unstable_Grid2"; // Grid version 2
import Typography from "@mui/material/Typography";
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
import { styled } from "@mui/material/styles";
import Tooltip, { TooltipProps, tooltipClasses } from "@mui/material/Tooltip";
import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
import Collapse from "@mui/material/Collapse";
import Divider from "@mui/material/Divider";
import CircularProgress from "@mui/material/CircularProgress";
import Stack from "@mui/material/Stack";
import CircleIcon from "@mui/icons-material/Circle";
import CustomCollapse from "@/components/Transform/Collapse";

import CodeBlock from "@/components/CodeBlock";
import {
  getTimestampOfPreviousDays,
  uppercaseFirstLetterOfEachWord,
  transformMs2TimeUnit,
  getSQlSubLabel,
  executeSql,
  pingDB,
  numberWithCommas,
  transformTimeUnit2Ms, mappingStatusNames,
} from '@/utils';
import SQLStatus from "@/components/Status/SQLStatus";
import SQLResultTable from "@/components/Table/SQLResultTable";
import { mysqlLatencyState, tidbLatencyState } from "@/recoil/atoms";
import { useRecoilState } from "recoil";
import type { InstanceInfo } from "@/pages/api/instances/[dbType]";
import type { SQLResult as SQLResultType } from "@/pages/api/sql/run";
import { CircleStack, VersusIcon } from "@/components/Sections/Scalable/Icons";
import { ResultCardHeader } from "@/components/Sections/Scalable/EasyScaleContent_v2/Common";
import {
  SQLComment,
  timerangeLabelMap,
} from "@/components/Sections/RealTimeAnalytics/_config";
import type { TimeRangeLabelType } from "@/components/Sections/RealTimeAnalytics/_config";

function getParamsByTimerangeLabel(label?: TimeRangeLabelType) {
  switch (label) {
    case "last1day":
      return {
        start_time: getTimeOfPreviousDays(1),
        end_time: getTimeOfPreviousDays(0),
      };
    case "last7days":
      return {
        start_time: getTimeOfPreviousDays(7),
        end_time: getTimeOfPreviousDays(0),
      };
    case "last12hours":
      return {
        start_time: getTimeOfPreviousHours(12),
        end_time: getTimeOfPreviousHours(0),
      };
    case "last6hours":
      return {
        start_time: getTimeOfPreviousHours(6),
        end_time: getTimeOfPreviousHours(0),
      };
    case "last30days":
    default:
      return {
        start_time: getTimeOfPreviousDays(30),
        end_time: getTimeOfPreviousDays(0),
      };
  }
}

export default function SQL(props: {
  sql: string;
  example?: string;
  hideSelect?: boolean;
  defaultTimerange?: TimeRangeLabelType;
  defaultTimeranges?: TimeRangeLabelType[];
}) {
  const {
    sql,
    example,
    hideSelect,
    defaultTimerange = "last6hours",
    defaultTimeranges,
  } = props;

  // Note: subLabel is used to fine-grained tracing the SQL API calling.
  const [subLabel, setSubLabel] = React.useState(
    getSQlSubLabel(example, {
      timeRange: defaultTimerange,
      top_n: "10",
    })
  );
  const [params, setParams] = React.useState({
    ...getParamsByTimerangeLabel(defaultTimerange),
    top_n: "10",
  });
  const [sqlStr, setSqlStr] = React.useState(sql);
  const [hideCode, setHideCode] = React.useState(true);
  const [tidbLoading, setTidbLoading] = React.useState(false);
  const [tidbResult, setTidbResult] = React.useState<SQLResultType | null>(
    null
  );
  const [tidbErrorMsg, setTidbErrorMsg] = React.useState<string | null>(null);
  const [mysqlLoading, setMysqlLoading] = React.useState(false);
  const [mysqlResult, setMysqlResult] = React.useState<SQLResultType | null>(
    null
  );
  const [mysqlErrorMsg, setMysqlErrorMsg] = React.useState<string | null>(null);

  const [mysqlLatency, setMysqlLatency] = useRecoilState(mysqlLatencyState);
  const [tidbLatency, setTidbLatency] = useRecoilState(tidbLatencyState);

  const handleCfgChange = ({
    top,
    timerange,
  }: {
    top: string;
    timerange: string;
  }) => {
    const newParams = {
      top_n: top,
      ...getParamsByTimerangeLabel(timerange as TimeRangeLabelType),
    };
    setParams(newParams);
    setSubLabel(
      getSQlSubLabel(example, {
        timeRange: timerange,
        top_n: top,
      })
    );
  };

  React.useEffect(() => {
    const newSqlStr = sql
      .replaceAll("${top_n}", params.top_n)
      .replaceAll("${start_time}", params.start_time)
      .replaceAll("${end_time}", params.end_time);
    setSqlStr(newSqlStr);
  }, [params, sql]);

  const handleRun = async () => {
    setHideCode(false);
    await Promise.all([handleTidbRun(), handleMysqlRun()]);
  };

  const handleTidbRun = async () => {
    try {
      setTidbResult(null);
      setTidbLoading(true);
      setTidbErrorMsg(null);
      const data = await executeSql(sqlStr, false, subLabel);
      setTidbResult(data);
      process.env.NODE_ENV === "development" && console.log(data);
    } catch (error: any) {
      console.error(error);
      const { message, sqlMessage } = error;
      setTidbErrorMsg(`${message}${sqlMessage ? `\n${sqlMessage}` : ""}`);
    } finally {
      setTidbLoading(false);
    }
  };

  const handleMysqlRun = async () => {
    try {
      setMysqlResult(null);
      setMysqlLoading(true);
      setMysqlErrorMsg(null);
      const data = await executeSql(sqlStr, true, subLabel);
      setMysqlResult(data);
      process.env.NODE_ENV === "development" && console.log(data);
    } catch (error: any) {
      console.error(error);
      const { message, sqlMessage } = error;
      setMysqlErrorMsg(`${message}${sqlMessage ? `\n${sqlMessage}` : ""}`);
    } finally {
      setMysqlLoading(false);
    }
  };

  const handlePing = async () => {
    const types: ("tidb" | "mysql")[] = ["tidb", "mysql"];
    for (const type of types) {
      try {
        const latency = await pingDB(type);
        if (type === "tidb") {
          setTidbLatency(latency);
        } else if (type === "mysql") {
          setMysqlLatency(latency);
        }
      } catch (error) {
        console.error(error);
        if (type === "tidb") {
          setTidbLatency(null);
        } else if (type === "mysql") {
          setMysqlLatency(null);
        }
      }
    }
  };

  const showTidbColdStartTipMemo = React.useMemo(() => {
    const tidbExecTime =
      transformTimeUnit2Ms(tidbResult?.plan?.databaseCostTime || '') ||
      tidbResult?.costTime;
    const mysqlExecTime =
      transformTimeUnit2Ms(mysqlResult?.plan?.databaseCostTime || '') ||
      mysqlResult?.costTime;
    if (!tidbExecTime && mysqlExecTime) {
      return true;
    }
    if (tidbExecTime && mysqlExecTime && tidbExecTime > mysqlExecTime) {
      return true;
    }
    return false;
  }, [
    mysqlResult?.costTime,
    mysqlResult?.plan?.databaseCostTime,
    tidbResult?.costTime,
    tidbResult?.plan?.databaseCostTime,
  ]);

  React.useEffect(() => {
    const pingInterval = setInterval(() => {
      handlePing();
    }, 60000);
    handlePing();

    return () => {
      clearInterval(pingInterval);
      setTidbLatency(null);
      setMysqlLatency(null);
    };
  }, []);

  return (
    <>
      <Grid xs={12}>
        <MenuBar
          onChange={handleCfgChange}
          handleRun={handleRun}
          loading={tidbLoading}
          hideTopN={sql.indexOf('${top_n}') === -1 || hideSelect}
          hideTimerange={hideSelect}
          defaultTimerange={defaultTimerange}
          defaultTimeranges={defaultTimeranges}
        />
      </Grid>
      <Grid xs={12}>
        <CustomCollapse
          in={hideCode}
          handleChange={() => {
            setHideCode((prev) => !prev);
          }}
          collapsedSize={100}
        >
          <CodeBlock
            language='sql'
            text={SQLComment + sqlStr}
            boxSxProps={{
              backgroundColor: '#FFFFFF',
              borderWidth: '1px',
              borderStyle: 'solid',
              borderColor: (theme) => theme.palette.line?.primary,
              borderRadius: '6px',
            }}
          />
        </CustomCollapse>
      </Grid>
      <Grid xs={12} md={6}>
        <ResultBlockTemplate
          title='MySQL RDS'
          type='mysql'
          tooltipItems={descItems.mysql}
          loading={mysqlLoading}
          data={mysqlResult}
          errorMsg={mysqlErrorMsg}
          status={mysqlLatency}
        />
      </Grid>
      <Grid xs={12} md={6}>
        <ResultBlockTemplate
          title='TiDB Cloud Starter'
          type='tidb'
          tooltipItems={descItems.tidb}
          data={tidbResult}
          loading={tidbLoading}
          errorMsg={tidbErrorMsg}
          status={tidbLatency}
          showTip={showTidbColdStartTipMemo}
          tip={
            <>
              <Box
                sx={{
                  width: '100%',
                  padding: '16px',
                  marginTop: '16px',
                  background: 'rgba(9, 214, 152, 0.07)',
                  border: '1px solid rgba(9, 214, 152, 0.2)',
                  borderRadius: '6px',
                  display: 'flex',
                  alignItems: 'flex-start',
                  flexDirection: 'column',
                  fontFamily: "'IBM Plex Mono', sans-serif;",

                  '& > div:not(:first-of-type)': {
                    pl: '28px',
                  },
                }}
              >
                <Typography color='info.main' fontFamily='inherit'>
                  For cost-saving purposes, analytics nodes in TiDB Cloud Starter
                  will be <strong> suspended</strong> if they are not used for a
                  long time.
                </Typography>
                <br />
                <Typography color='info.main' fontFamily='inherit'>
                  When the initial query comes in, TiDB Cloud Starter will
                  <strong> cold-start</strong> the analytics node, resulting in
                  an increased latency for the query, so please retry again
                  later (about ten seconds or so).
                </Typography>
              </Box>
            </>
          }
        />
      </Grid>
    </>
  );
}

export const HtmlTooltip = styled(({ className, ...props }: TooltipProps) => (
  <Tooltip arrow {...props} classes={{ popper: className }} />
))(({ theme }) => ({
  [`& .${tooltipClasses.tooltip}`]: {
    backgroundColor: "#f5f5f9",
    color: "rgba(0, 0, 0, 0.87)",
    maxWidth: 300,
    fontSize: theme.typography.pxToRem(12),
    border: "1px solid #dadde9",
  },
}));

const descItems = {
  mysql: [
    {
      id: "type",
      label: "Type",
      content: "RDS for MySQL db.t4g.large",
    },
    {
      id: "host",
      label: "Host",
      content: "playground.c4kguokl7dh7.us-west-2.rds.amazonaws.com",
    },
    {
      id: "region",
      label: "Region",
      content: "Oregon (us-west-2b)",
    },
  ],
  tidb: [
    {
      id: "type",
      label: "Type",
      content: "TiDB Cloud Starter",
    },
    {
      id: "host",
      label: "Host",
      content: "gateway01.us-west-2.prod.aws.tidbcloud.com",
    },
    {
      id: "region",
      label: "Region",
      content: "Oregon (us-west-2)",
    },
  ],
};

function DescItemGroups(props: {
  items: {
    id: string;
    label: string;
    content: string | React.ReactNode;
  }[];
}) {
  const { items } = props;

  return (
    <>
      <Stack
        component="ul"
        direction="column"
        gap={1}
        sx={{
          listStyleType: "none",
        }}
      >
        {items.map((item) => (
          <Typography component="li" key={item.id} variant="body2">
            <Typography component="span" variant="body2" fontWeight="bold">
              {item.label}:{` `}
            </Typography>
            {item.content}
          </Typography>
        ))}
      </Stack>
    </>
  );
}

function StatusIcon(props: {
  status: InstanceInfo["status"] | "faulty";
  boxSx?: BoxProps["sx"];
  afterIcon?: React.ReactNode;
}) {
  const { status, boxSx, afterIcon } = props;

  return (
    <Box
      sx={{
        borderRadius: "6px",
        padding: "2px 8px",
        display: "inline-flex",
        alignItems: "center",
        gap: "4px",
        userSelect: "none",
        cursor: "default",
        backgroundColor: (theme) => theme.palette.background?.paper,
        ...(status === "available" && {
          backgroundColor: "rgba(9, 214, 152, 0.1)",
          color: "#09D698",
        }),
        ...(status === "unavailable" && {
          backgroundColor: "rgba(237, 18, 18, 0.1)",
          color: "#ED1212",
        }),
        ...(status === "faulty" && {
          backgroundColor: "rgba(239, 192, 23, 0.1)",
          color: "#FDD134",
        }),
        ...boxSx,
      }}
    >
      <CircleIcon
        fontSize="inherit"
        sx={{
          transform: "scale(0.5)",
        }}
      />
      <Typography fontSize={16} lineHeight={1.5} component="span">
        {uppercaseFirstLetterOfEachWord(mappingStatusNames(status))}
      </Typography>
      {afterIcon}
    </Box>
  );
}

function StatusTip(props: { status: InstanceInfo; boxSx?: BoxProps["sx"] }) {
  const { status, boxSx } = props;

  return (
    <HtmlTooltip
      title={
        <>
          {status.networkLatency && (
            <Typography
              component="div"
              fontWeight="bold"
              fontSize="14px"
              lineHeight="17px"
            >
              {`Latency: ${transformMs2TimeUnit(status.networkLatency)}`}
            </Typography>
          )}
        </>
      }
    >
      <StatusIcon status={status.status} boxSx={boxSx} />
    </HtmlTooltip>
  );
}

function MenuBar(props: {
  onChange: ({ top, timerange }: { top: string; timerange: string }) => void;
  handleRun: () => void;
  loading?: boolean;
  hideTopN?: boolean;
  hideTimerange?: boolean;
  defaultTimerange?: TimeRangeLabelType;
  defaultTimeranges?: TimeRangeLabelType[];
}) {
  const [displaySize, setDisplaySize] = React.useState<string>("10");
  const [timerange, setTimerange] = React.useState<TimeRangeLabelType>(
    props.defaultTimerange || "last6hours"
  );

  const handleDisplaySizeChange = (event: SelectChangeEvent) => {
    setDisplaySize(event.target.value);
    props.onChange({ top: event.target.value as string, timerange });
  };
  const handleTimerangeChange = (event: SelectChangeEvent) => {
    setTimerange(event.target.value as any);
    props.onChange({ top: displaySize, timerange: event.target.value as any });
  };

  const timerangeListMemo = React.useMemo(() => {
    return (
      props.defaultTimeranges ||
      (Object.keys(timerangeLabelMap) as TimeRangeLabelType[])
    );
  }, [props.defaultTimeranges]);

  return (
    <Box display="flex" flexWrap="wrap" alignItems="center" gap={2}>
      {!props.hideTopN && (
        <Box display="flex" alignItems="center" gap={1}>
          <Typography component="span">Top</Typography>
          <FormControl sx={{ minWidth: 120 }} size="small">
            <StyledSelect
              value={displaySize}
              onChange={handleDisplaySizeChange}
            >
              <MenuItem value={10}>10</MenuItem>
              <MenuItem value={20}>20</MenuItem>
              <MenuItem value={30}>30</MenuItem>
            </StyledSelect>
          </FormControl>
        </Box>
      )}

      {!props.hideTimerange && (
        <Box display="flex" alignItems="center" gap={1}>
          <Typography component="span">Time range</Typography>
          <FormControl sx={{ minWidth: 120 }} size="small">
            <StyledSelect value={timerange} onChange={handleTimerangeChange}>
              {timerangeListMemo.map((timerange) => (
                <MenuItem key={timerange} value={timerange}>
                  {timerangeLabelMap[timerange]}
                </MenuItem>
              ))}
              {/* <MenuItem value={"last30days"}>30 days</MenuItem>
              <MenuItem value={"last7days"}>7 days</MenuItem>
              <MenuItem value={"last1day"}>1 day</MenuItem>
              <MenuItem value={"last12hours"}>12 hours</MenuItem>
              <MenuItem value={"last6hours"}>6 hours</MenuItem> */}
            </StyledSelect>
          </FormControl>
        </Box>
      )}

      <Box>
        <LoadingButton
          loading={props.loading}
          variant="contained"
          size="small"
          loadingPosition="start"
          startIcon={<PlayArrowIcon fontSize="inherit" />}
          onClick={props.handleRun}
        >
          Run SQL
        </LoadingButton>
      </Box>
    </Box>
  );
}

const StyledSelect = styled((props: SelectProps<string>) => (
  <Select {...props} />
))(({ theme }) => ({
  "& .MuiSelect-select": {
    padding: "4px 0 4px 8px",
    backgroundColor: theme.palette.background.paper,
  },
  // "&:before": {
  //   display: "none",
  // },
  // "&:after": {
  //   display: "none",
  // },
}));

export function ResultBlockTemplate(props: {
  title: string | React.ReactNode;
  type?: "default" | "mysql" | "tidb";
  data?: SQLResultType | null;
  loading?: boolean;
  errorMsg?: string | null;
  status?: InstanceInfo | null;
  afterIcon?: React.ReactNode;
  tooltipItems?: {
    id: string;
    label: string;
    content: string | React.ReactNode;
  }[];
  showTip?: boolean;
  tip?: React.ReactNode | string;
}) {
  const {
    title,
    data,
    loading,
    errorMsg,
    status,
    tooltipItems = [],
    type = "default",
    afterIcon,
    showTip,
    tip,
  } = props;

  const tooltipItemsMemo = React.useMemo(() => {
    const results = [...tooltipItems];
    if (status?.networkLatency) {
      results.push({
        id: "latency",
        label: "Network Latency",
        content: transformMs2TimeUnit(status?.networkLatency || 0),
      });
    }
    return results;
  }, [tooltipItems, status]);

  return (
    <Box>
      <ResultCardHeader type={type}>
        <Typography
          component='div'
          display='inline-flex'
          alignItems='center'
          gap={1}
        >
          <Typography component='span' fontWeight='bold'>
            {title}
          </Typography>
          {(status || tooltipItems.length > 0) && (
            <>
              <HtmlTooltip title={<DescItemGroups items={tooltipItemsMemo} />}>
                <Box>
                  {status?.status ? (
                    <StatusIcon
                      status={
                        status?.networkLatency && status.networkLatency > 200
                          ? 'faulty'
                          : status.status
                      }
                      afterIcon={
                        <InfoOutlinedIcon fontSize='inherit' color='inherit' />
                      }
                    />
                  ) : (
                    <InfoOutlinedIcon fontSize='inherit' />
                  )}
                </Box>
              </HtmlTooltip>
            </>
          )}
          {afterIcon}
          {status?.cost && <Typography marginLeft='auto'>
            {`≈ ${status.cost} USD/Month`}
          </Typography>}
        </Typography>
      </ResultCardHeader>
      <Box
        sx={{
          width: '100%',
          height: '100%',
          minHeight: '250px',
          backgroundColor: (theme) => theme.palette.background.paper,
          padding: '26px',
          ...(!data &&
            !errorMsg && {
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
          }),
          ...(!data && loading && {
            display: 'flex',
            flexDirection: 'column',
            alignItems: 'center',
            justifyContent: 'center',
          })
        }}
      >
        {!data && !errorMsg && (
          <Typography
            component='div'
            fontWeight='bold'
            fontSize='24px'
            lineHeight='29px'
            color='text.light'
            sx={{
              cursor: 'default',
              textAlign: 'center',
            }}
          >
            {loading ? (
              <>
                <CircularProgress size='24px' /> Running
              </>
            ) : (
              <>your results will display here</>
            )}
          </Typography>
        )}
        {!data && loading && (
          <Typography
            component='div'
            fontWeight='bold'
            fontSize='24px'
            lineHeight='29px'
            color='text.light'
            sx={{
              cursor: 'default',
            }}
          ></Typography>
        )}
        {data && (
          <Box
            display='flex'
            flexDirection='column'
            gap={1}
            fontFamily="'IBM Plex Mono', sans-serif"
          >
            <Box
              sx={{
                width: '100%',
                padding: '16px',
                background: 'rgba(9, 214, 152, 0.07)',
                border: '1px solid rgba(9, 214, 152, 0.2)',
                borderRadius: '6px',
                display: 'flex',
                alignItems: 'flex-start',
                flexDirection: 'column',

                '& > div:not(:first-of-type)': {
                  pl: '28px',
                },
              }}
            >
              <SQLStatus
                status='success'
                label='Execution time'
                content={
                  data?.dbType === 'tidb'
                    ? `${data?.plan?.databaseCostTime}`
                    : `${numberWithCommas(data?.costTime)}ms`
                }
                boxSx={{ fontFamily: 'inherit' }}
              />
              {/*{!!data?.plan?.maxRowsScanned && (*/}
              {/*  <SQLStatus*/}
              {/*    label='Scan Rows'*/}
              {/*    content={`${numberWithCommas(data.plan.maxRowsScanned)}`}*/}
              {/*    boxSx={{ fontFamily: 'inherit' }}*/}
              {/*  />*/}
              {/*)}*/}
            </Box>
            <Divider />
            <Box color='#565656'>
              <SQLResultTable
                fields={data?.fields || []}
                rows={data?.rows || []}
              />
            </Box>
          </Box>
        )}
        {errorMsg && (
          <Box
            sx={{
              width: '100%',
              padding: '16px',
              background: 'rgba(9, 214, 152, 0.07)',
              border: '1px solid rgba(9, 214, 152, 0.2)',
              borderRadius: '6px',
              display: 'flex',
              alignItems: 'flex-start',
              flexDirection: 'column',
              fontFamily: "'IBM Plex Mono', sans-serif;",

              ...(errorMsg && {
                background: 'rgba(237, 18, 18, 0.05)',
                border: '1px solid rgba(237, 18, 18, 0.1)',
              }),

              '& > div:not(:first-of-type)': {
                pl: '28px',
              },
            }}
          >
            <Typography color='error.main' fontFamily='inherit'>
              {errorMsg}
            </Typography>
          </Box>
        )}
        {/*{showTip && <>{tip}</>}*/}
      </Box>
    </Box>
  );
}

function getTimeOfPreviousDays(previousDays = 1) {
  // Get the current date
  const currentDate = new Date("30 Nov 2019 23:59:59 GMT");

  // Subtract one day from the current date
  const previousDate = new Date(currentDate);
  previousDate.setDate(currentDate.getDate() - previousDays);

  const time = previousDate.toISOString().slice(0, 19).replace("T", " ");
  return time;
}

function getTimeOfPreviousHours(previousHours = 1) {
  // Get the current date
  const currentDate = new Date("30 Nov 2019 23:59:59 GMT");

  // Subtract one day from the current date
  const previousDate = new Date(currentDate);
  previousDate.setHours(currentDate.getHours() - previousHours);

  const time = previousDate.toISOString().slice(0, 19).replace("T", " ");
  return time;
}
