import * as React from "react";
import Timeline from "@mui/lab/Timeline";
import TimelineItem, { timelineItemClasses } from "@mui/lab/TimelineItem";
import TimelineSeparator from "@mui/lab/TimelineSeparator";
import TimelineConnector from "@mui/lab/TimelineConnector";
import TimelineContent from "@mui/lab/TimelineContent";
import TimelineDot from "@mui/lab/TimelineDot";
import Grid from "@mui/material/Unstable_Grid2";
import Box, { BoxProps } from "@mui/material/Box";
import Button from "@mui/material/Button";
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
import RestartAltIcon from "@mui/icons-material/RestartAlt";
import Typography from "@mui/material/Typography";

import { Note } from "@/components/Alert";
import SQLCard, {
  SQLResultPreviewCard,
  SQLResultPreviewCardTableProps,
} from "@/components/Card/SQLCard";
import CodeBlock from "@/components/CodeBlock";

const TransactionSqlCard = (props: { sql: string }) => {
  const { sql } = props;

  return (
    <Box
      sx={{
        textAlign: "left",
        py: 1,
      }}
    >
      <CodeBlock language="sql" text={sql} />
    </Box>
  );
};

const DiffSteps: {
  id: string;
  boxSx?: BoxProps["sx"];
  left?: {
    label: string;
    description: React.ReactNode | null;
    note?: string;
    boxSx?: BoxProps["sx"];
    data?: SQLResultPreviewCardTableProps;
  };
  right?: {
    label: string;
    description: React.ReactNode | null;
    note?: string;
    boxSx?: BoxProps["sx"];
    data?: SQLResultPreviewCardTableProps;
  };
}[] = [
  {
    id: "1",
    left: {
      label: "Start Transaction A.",
      description: <code>begin</code>,
      boxSx: {
        minHeight: 326,
      },
    },
  },
  {
    id: "2",
    right: {
      label: "Start Transaction B.",
      description: <code>begin</code>,
    },
  },
  {
    id: "3",
    boxSx: {
      // minHeight: 264,
    },
    right: {
      label: "Transaction B: Retrieve the latest stock & price of the product",
      description: (
        <TransactionSqlCard
          sql={`SELECT product_id, stock, price
FROM products
WHERE product_id = 10001
FOR UPDATE`}
        />
      ),
      // note: `Got the latest price of product is $400 and 4 items left in stock. (Got the lock) As there is enough inventory, it can proceed with the next steps.`,
      data: {
        fields: [
          { name: "product_id", columnType: 0 },
          { name: "stock", columnType: 0 },
          { name: "price", columnType: 0 },
        ],
        rows: [{ product_id: "10001", stock: "4", price: "400" }],
      },
    },
  },
  {
    id: "4",
    boxSx: {
      minHeight: 835,
    },
    left: {
      label: "Transaction A: Retrieve the latest stock & price of the product",
      description: (
        <TransactionSqlCard
          sql={`SELECT product_id, price
FROM products
WHERE product_id = 10001
FOR UPDATE`}
        />
      ),
      note: `Attempt to retrieve the latest price of the product, but be blocked. (Because wait to get the lock)`,
    },
  },
  {
    id: "5",
    boxSx: {
      // minHeight: 224,
    },
    right: {
      label: "Transaction B: Update the stock of the product",
      description: (
        <>
          <TransactionSqlCard
            sql={`UPDATE products
SET stock = stock - 4
WHERE product_id = 10001 AND stock - 4 >= 0`}
          />
          <TransactionSqlCard
            sql={`SELECT product_id, stock, price
FROM products
WHERE product_id = 10001`}
          />
        </>
      ),
      data: {
        fields: [
          { name: "product_id", columnType: 0 },
          { name: "stock", columnType: 0 },
          { name: "price", columnType: 0 },
        ],
        rows: [{ product_id: "10001", stock: "0", price: "400" }],
      },
    },
  },
  {
    id: "6",
    boxSx: {
      // minHeight: 164,
    },
    right: {
      label: "Transaction B: Insert a new order",
      description: (
        <TransactionSqlCard
          sql={`INSERT INTO orders (user_id, product_id, quality)
VALUES (1001, 10001, 4)`}
        />
      ),
    },
  },
  {
    id: "7",
    boxSx: {
      // minHeight: 204,
    },
    right: {
      label: "Transaction B: Update the balance of the user account",
      description: (
        <TransactionSqlCard
          sql={`UPDATE users
SET balance = balance - 400
WHERE id = 1001`}
        />
      ),
    },
  },
  {
    id: "8",
    boxSx: {},
    right: {
      label: "Transaction B: Commit the transaction",
      description: <code>COMMIT;</code>,
    },
  },
  {
    id: "9",
    boxSx: {},
    left: {
      label: "Transaction A: Get the latest stock & price of the product",
      description: <></>,
      note: `Recover from blocking, get the latest price of the product 400, but the remaining stock is 0. Due to insufficient stock, cancel the operation and roll back the transaction.`,
      data: {
        fields: [
          { name: "product_id", columnType: 0 },
          { name: "stock", columnType: 0 },
          { name: "price", columnType: 0 },
        ],
        rows: [{ product_id: "10001", stock: "0", price: "400" }],
      },
    },
  },
  {
    id: "10",
    boxSx: {},
    left: {
      label: "Transaction A: Rollback the transaction",
      description: (
        <>
          <code>ROLLBACK;</code>
        </>
      ),
    },
  },
];

export default function AutoPlayTransactionTimeline() {
  const [activeStep, setActiveStep] = React.useState(-1);
  const [isPlaying, setIsPlaying] = React.useState(false);
  const [shouldScrollIntoView, setShouldScrollIntoView] = React.useState(false);

  const handleNext = () => {
    setActiveStep((prevActiveStep) => prevActiveStep + 1);
  };

  const handleBack = () => {
    setActiveStep((prevActiveStep) => prevActiveStep - 1);
  };

  const handleReset = () => {
    setActiveStep(-1);
    setIsPlaying(false);
  };

  const handlePlay = () => {
    setActiveStep(0);
    setIsPlaying(true);
    !shouldScrollIntoView && setShouldScrollIntoView(true);
  };

  const latestRightNodeRef = React.useRef<HTMLDivElement>(null);
  const latestLeftNodeRef = React.useRef<HTMLDivElement>(null);

  React.useEffect(() => {
    if (isPlaying) {
      const timer = setInterval(() => {
        setActiveStep((prevActiveStep) =>
          prevActiveStep === DiffSteps.length - 1
            ? DiffSteps.length - 1
            : prevActiveStep + 1
        );
      }, 2000);
      return () => {
        clearInterval(timer);
      };
    }
  }, [isPlaying]);

  React.useEffect(() => {
    if (shouldScrollIntoView) {
      const left = latestLeftNodeRef.current?.getBoundingClientRect().top || 0;
      const right =
        latestRightNodeRef.current?.getBoundingClientRect().top || 0;
      console.log("left", left);
      console.log("right", right);
      console.log("top:", left > right ? "left" : "right");
      left > right
        ? latestLeftNodeRef?.current?.scrollIntoView({
            behavior: "smooth",
            block: "center",
            inline: "center",
          })
        : latestRightNodeRef?.current?.scrollIntoView({
            behavior: "smooth",
            block: "center",
            inline: "center",
          });
    }
  }, [activeStep, shouldScrollIntoView]);

  React.useEffect(() => {
    const stopScrollFunc = () => {
      shouldScrollIntoView && setShouldScrollIntoView(false);
    };
    document.addEventListener("wheel", stopScrollFunc, false);
    return () => {
      document.removeEventListener("wheel", stopScrollFunc);
    };
  }, [shouldScrollIntoView]);

  return (
    <Grid
      container
      spacing={2}
      mb={(theme) => theme.spacing(1)}
      id="transaction-timeline-container"
    >
      <Grid xs={12}>
        <Box>
          {!isPlaying && (
            <Button
              variant="contained"
              startIcon={<PlayArrowIcon />}
              onClick={handlePlay}
            >
              Play
            </Button>
          )}
          {isPlaying && (
            <Button
              variant="contained"
              startIcon={<RestartAltIcon />}
              onClick={handleReset}
            >
              Reset
            </Button>
          )}
        </Box>
      </Grid>
      {/* left */}
      <Grid xs={6}>
        <Timeline
          position="left"
          sx={{
            [`& .${timelineItemClasses.root}:before`]: {
              flex: 0,
              padding: 0,
            },
          }}
        >
          {DiffSteps.slice(0, activeStep + 1).map((steps, index) => {
            const step = steps?.left;
            if (!step) {
              return null;
            }
            return (
              <TimelineItem key={`left-${steps.id}`}>
                <TimelineSeparator>
                  <TimelineDot
                    variant="outlined"
                    color="primary"
                    ref={latestLeftNodeRef}
                  />
                  <TimelineConnector />
                </TimelineSeparator>
                <TimelineContent>
                  <CommonTimelineContent
                    step={step}
                    boxSx={{
                      ...({
                        ...steps?.boxSx,
                        ...steps?.left?.boxSx,
                      } as BoxProps["sx"]),
                    }}
                  />
                </TimelineContent>
              </TimelineItem>
            );
          })}
        </Timeline>
      </Grid>
      {/* right */}
      <Grid xs={6}>
        <Timeline
          position="right"
          sx={{
            [`& .${timelineItemClasses.root}:before`]: {
              flex: 0,
              padding: 0,
            },
          }}
        >
          {DiffSteps.slice(0, activeStep + 1).map((steps, index) => {
            const step = steps?.right;
            if (!step && index === 0) {
              return (
                <TimelineItem
                  key={`right-${steps.id}`}
                  ref={latestRightNodeRef}
                >
                  <TimelineContent></TimelineContent>
                </TimelineItem>
              );
            }
            if (!step) {
              return null;
            }
            return (
              <TimelineItem key={`right-${steps.id}`}>
                <TimelineSeparator>
                  <TimelineDot
                    variant="outlined"
                    color="primary"
                    ref={latestRightNodeRef}
                  />
                  <TimelineConnector />
                </TimelineSeparator>
                <TimelineContent>
                  <CommonTimelineContent
                    step={step}
                    boxSx={{
                      ...({
                        ...steps?.boxSx,
                        ...steps?.right?.boxSx,
                      } as BoxProps["sx"]),
                    }}
                  />
                </TimelineContent>
              </TimelineItem>
            );
          })}
        </Timeline>
      </Grid>
    </Grid>
  );
}

const CommonTimelineContent = (props: { step: any; boxSx: BoxProps["sx"] }) => {
  const { boxSx, step } = props;

  return (
    <>
      <Typography variant="body2" color="text.secondary">
        {step.label}
      </Typography>
      <Box sx={boxSx}>
        {step.description && (
          <Box
            pt={1}
            sx={{
              "& .prismjs": {
                textAlign: "left",
              },
            }}
          >
            {step.description}
          </Box>
        )}
        {step.data && (
          <Box pt={1} width="100%" textAlign="left">
            <SQLResultPreviewCard {...step.data} />
          </Box>
        )}
        {step.note && (
          <Box pt={1} textAlign="left">
            <Note>{step.note}</Note>
          </Box>
        )}
      </Box>
    </>
  );
};
