import * as React from "react";
import Box from "@mui/material/Box";
import Grid from "@mui/material/Unstable_Grid2"; // Grid version 2
import { useSnackbar } from "notistack";
import Typography from "@mui/material/Typography";
import NextLink from "next/link";
import LoadingButton from "@mui/lab/LoadingButton";
import Chip from "@mui/material/Chip";

import CancelIcon from "@mui/icons-material/Cancel";
import CheckCircleIcon from "@mui/icons-material/CheckCircle";

import { commonGridContainerProps } from "@/components/CodeBlock/SQL";
import SQLCard, {
  SQLStatusCard,
  SQLResultPreviewCard,
  LoadingCard,
} from "@/components/Card/SQLCard";
import { IndexTip } from "@/components/Status/SQLStatus";
import { Note } from "@/components/Alert";

import {
  calcPercentage,
  transformTimeUnit2Ms,
  calcPercentageBetween2Str,
  executeSql,
} from "@/utils";
import { URL_DOC_TIKV, URL_DOC_TIFLASH } from "@/constants/url";
import type { SQLResult as SQLResultType } from "@/pages/api/sql/run";

export interface SQLDiffProps {
  sql1: string;
  sql2: string;
  title1?: string | JSX.Element;
  title2?: string | JSX.Element;
  sql1Note?: string | JSX.Element;
  sql2Note?: string | JSX.Element;
  sql1StatusContent?: string | JSX.Element;
  sql2StatusContent?: string | JSX.Element;
  mode?: "default" | "compare";
  showCompareTitleIcon?: boolean;
  showCompareTitleBetterIcon?: boolean;
  wrapLongLines?: boolean;
}

export type SQLFieldsType = {
  name: string;
  columnType: number;
};

export type SQLPlanType = {
  databaseCostTime: string;
  maxRowsScanned: number;
  usedEngines: string[];
  usedIndexes: { tableName: string; indexFullName: string }[];
  steps: {
    [x: string]: string;
  }[];
};

export type SQLResultRowType = {
  [x: string]: string;
};

// TODO refactor this with src/components/CodeBlock/SQL.tsx
export default function SQLDiff(props: SQLDiffProps) {
  const {
    sql1,
    sql2,
    title1,
    title2,
    sql1Note,
    sql2Note,
    sql1StatusContent,
    sql2StatusContent,
    mode = "default",
    showCompareTitleIcon,
    showCompareTitleBetterIcon,
    wrapLongLines,
  } = props;
  const [sql1Result, setSql1Result] = React.useState<SQLResultType | null>(
    null
  );
  const [sql2Result, setSql2Result] = React.useState<SQLResultType | null>(
    null
  );
  const [sql1Loading, setSql1Loading] = React.useState(false);
  const [sql2Loading, setSql2Loading] = React.useState(false);
  const [sql1ErrorMsg, setSql1ErrorMsg] = React.useState("");
  const [sql2ErrorMsg, setSql2ErrorMsg] = React.useState("");

  const { enqueueSnackbar } = useSnackbar();

  const handleSql1Click = async () => {
    setSql1Loading(true);
    setSql1Result(null);
    setSql1ErrorMsg("");
    try {
      const data = await executeSql(sql1);
      setSql1Result(data);
    } catch (error: any) {
      console.error(error);
      setSql1ErrorMsg(`${error?.message} ${error?.sqlMessage}`);
      enqueueSnackbar(`${error?.message}}`, {
        variant: "error",
      });
    } finally {
      setSql1Loading(false);
    }
  };

  const handleSql2Click = async () => {
    setSql2Loading(true);
    setSql2Result(null);
    setSql2ErrorMsg("");
    try {
      const data = await executeSql(sql2);
      setSql2Result(data);
    } catch (error: any) {
      console.error(error);
      setSql2ErrorMsg(`${error?.message} ${error?.sqlMessage}`);
      enqueueSnackbar(`${error?.message}`, {
        variant: "error",
      });
    } finally {
      setSql2Loading(false);
    }
  };

  const handleRunClick = async () => {
    await Promise.all([handleSql1Click(), handleSql2Click()]);
  };

  return (
    <Grid container {...commonGridContainerProps}>
      {mode === "compare" && (
        <Grid xs={12}>
          <LoadingButton
            variant="contained"
            onClick={handleRunClick}
            loading={sql1Loading || sql2Loading}
          >
            Run
          </LoadingButton>
        </Grid>
      )}
      <Grid
        xs={6}
        sx={{
          display: "flex",
          flexDirection: "column",
        }}
      >
        {title1 && (
          <Typography
            fontWeight="bold"
            fontSize="16px"
            lineHeight="19px"
            color="grey.900"
            display="inline-flex"
            alignItems="center"
            gap={1}
          >
            {showCompareTitleIcon && <CancelIcon sx={{ fill: "#FF6F50" }} />}
            <Box color={showCompareTitleIcon ? "#838383" : "inherit"}>
              {title1}
            </Box>
          </Typography>
        )}
        {sql1Note && <>{sql1Note}</>}
        <SQLCard
          sql={sql1}
          onClick={handleSql1Click}
          loading={sql1Loading}
          errorMsg={sql1ErrorMsg}
          disableRun={mode === "compare"}
          mode={mode === "compare" ? "light" : "default"}
          wrapLongLines={wrapLongLines}
        />
      </Grid>
      <Grid
        xs={6}
        sx={{
          display: "flex",
          flexDirection: "column",
        }}
      >
        {title2 && (
          <Typography
            fontWeight="bold"
            fontSize="16px"
            lineHeight="19px"
            color="grey.900"
            display="inline-flex"
            alignItems="center"
            gap={1}
          >
            {showCompareTitleIcon && (
              <CheckCircleIcon sx={{ fill: "#09D698" }} />
            )}
            <Box color="inherit">{title2}</Box>
            {showCompareTitleBetterIcon && (
              <Chip
                label="Better"
                size="small"
                sx={{ backgroundColor: "#09D698", color: "#fff" }}
              />
            )}
          </Typography>
        )}
        {sql2Note && <>{sql2Note}</>}
        <SQLCard
          sql={sql2}
          onClick={handleSql2Click}
          loading={sql2Loading}
          disabled={!sql1Result}
          errorMsg={sql2ErrorMsg}
          disableRun={mode === "compare"}
          wrapLongLines={wrapLongLines}
        />
      </Grid>
      <Grid xs={6}>
        {sql1Loading && <LoadingCard />}
        {!sql1Loading && sql1Result?.plan && (
          <SQLStatusCard
            status={[
              {
                status: "success",
                label: "Processed",
                content: `${sql1Result.plan.maxRowsScanned.toLocaleString()} rows`,
              },
              sql1Result.plan.usedIndexes.length > 0
                ? {
                    status: "success",
                    label: "Used Index",
                    content: (
                      <>
                        {sql1Result.plan.usedIndexes.map((val) => (
                          <IndexTip
                            key={val.indexFullName}
                            content={val.indexFullName}
                          />
                        ))}
                      </>
                    ),
                  }
                : null,
              {
                status: "success",
                label: "Elapsed",
                content: sql1Result.plan.databaseCostTime,
              },
              {
                status: "success",
                label: "Engines",
                content: (
                  <>
                    <ExplainEngines engines={sql1Result.plan.usedEngines} />
                  </>
                ),
              },
            ]}
            data={sql1Result.plan.steps}
            content={sql1StatusContent}
            mode={mode === "compare" ? "light" : "default"}
          />
        )}
      </Grid>
      <Grid xs={6}>
        {sql2Loading && <LoadingCard />}
        {!sql2Loading && sql2Result?.plan && (
          <SQLStatusCard
            status={[
              {
                status: "success",
                label: "Processed",
                content: `${sql2Result.plan.maxRowsScanned.toLocaleString()} rows`,
                tip:
                  sql1Result?.plan &&
                  (sql2Result.plan.maxRowsScanned <
                  sql1Result.plan.maxRowsScanned
                    ? `-${calcPercentage(
                        sql1Result.plan.maxRowsScanned -
                          sql2Result.plan.maxRowsScanned,
                        sql1Result.plan.maxRowsScanned
                      )}% scanned rows`
                    : undefined),
              },
              sql2Result.plan.usedIndexes.length > 0
                ? {
                    status: "success",
                    label: "Used Index",
                    content: (
                      <>
                        {sql2Result.plan.usedIndexes.map((val) => (
                          <IndexTip
                            key={val.indexFullName}
                            content={val.indexFullName}
                          />
                        ))}
                      </>
                    ),
                  }
                : null,
              {
                status: "success",
                label: "Elapsed",
                content: sql2Result.plan.databaseCostTime,
                tip:
                  sql1Result?.plan &&
                  (transformTimeUnit2Ms(sql1Result.plan.databaseCostTime) >
                  transformTimeUnit2Ms(sql2Result.plan.databaseCostTime)
                    ? `-${calcPercentageBetween2Str(
                        sql1Result.plan.databaseCostTime,
                        sql2Result.plan.databaseCostTime
                      )}% processing time`
                    : undefined),
              },
              {
                status: "success",
                label: "Engines",
                content: (
                  <ExplainEngines engines={sql2Result.plan.usedEngines} />
                ),
              },
            ]}
            data={sql2Result.plan.steps}
            content={sql2StatusContent}
          />
        )}
      </Grid>
      <Grid xs={6}>
        {sql1Result && (
          <SQLResultPreviewCard
            fields={sql1Result.fields || []}
            rows={sql1Result.rows || []}
            mode={mode === "compare" ? "light" : "default"}
          />
        )}
      </Grid>
      <Grid xs={6}>
        {sql2Result && (
          <SQLResultPreviewCard
            fields={sql2Result.fields || []}
            rows={sql2Result.rows || []}
          />
        )}
      </Grid>
    </Grid>
  );
}

export function ExplainEngines(props: { engines: string[] }) {
  const { engines } = props;

  return (
    <Box>
      {engines.includes("tikv") && (
        <>
          row-based storage (
          <Typography component={NextLink} href={URL_DOC_TIKV} target="_blank">
            TiKV
          </Typography>
          ){` `}
        </>
      )}
      {engines.includes("tiflash") && (
        <>
          columnar storage (
          <Typography
            component={NextLink}
            href={URL_DOC_TIFLASH}
            target="_blank"
          >
            TiFlash
          </Typography>
          )
        </>
      )}
    </Box>
  );
}
