import * as React from "react";
import Box from "@mui/material/Box";
import TextField from "@mui/material/TextField";
import EditIcon from "@mui/icons-material/Edit";
import Grid from "@mui/material/Unstable_Grid2"; // Grid version 2
import Typography, { TypographyProps } from "@mui/material/Typography";
import Stack from "@mui/material/Stack";
import MenuItem from "@mui/material/MenuItem";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import LoadingButton from "@mui/lab/LoadingButton";
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
import { useRecoilState, useRecoilValue } from "recoil";
import { styled } from "@mui/material/styles";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell, { tableCellClasses } from "@mui/material/TableCell";
import TableContainer from "@mui/material/TableContainer";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import Paper from "@mui/material/Paper";
import Skeleton from "@mui/material/Skeleton";
import CircularProgress from "@mui/material/CircularProgress";
import { useSnackbar } from "notistack";
import Chip from "@mui/material/Chip";
import WarningIcon from "@mui/icons-material/Warning";

import CodeBlock, {
  UnifiedCodeDiffHighlight,
  CodeSnippet,
} from "@/components/CodeBlock";
import {
  retrieveBrandList,
  initCRUD,
  createProduct,
  retrieveProductList,
} from "@/utils/mysqlCompatible";
import { mysqlCompatibelCRUDInitState, sessionState } from "@/recoil/atoms";
import { useSession, useInitCRUD } from "@/utils/hook";
import type {
  SQLResult as SQLResultType,
  FieldInfo as FieldInfoType,
} from "@/pages/api/sql/run";

const SQL_CONTENT = `INSERT INTO products (
  product_name, price, brand_id, stock
) 
VALUES 
  ('\${NAME}', \${PRICE}, \${BRAND_ID}, \${STOCK});`;

export default function CreateContent(props: {}) {
  const [pdName, setPdName] = React.useState<string | null>(null);
  const [price, setPrice] = React.useState<number | null>(null);
  const [brand, setBrand] = React.useState<string | null>(null);
  const [stock, setStock] = React.useState<number | null>(null);
  const [sqlContent, setSqlContent] = React.useState<string>(SQL_CONTENT);
  const [pdNameError, setPdNameError] = React.useState<string | null>(null);
  const [priceError, setPriceError] = React.useState<string | null>(null);
  // const [brandError, setBrandError] = React.useState<string | null>(null);
  const [stockError, setStockError] = React.useState<string | null>(null);
  const [brandList, setBrandList] = React.useState<
    {
      brandId: string;
      brandName: string;
    }[]
  >([]);
  const [globalLoading, setGlobalLoading] = React.useState<boolean>(true);
  const [submitting, setSubmitting] = React.useState<boolean>(false);
  const [originalResult, setOriginalResult] =
    React.useState<SQLResultType | null>(null);
  const [result, setResult] = React.useState<SQLResultType | null>(null);
  const [postResult, setPostResult] = React.useState<SQLResultType | null>(
    null
  );

  // const [isInitCRUD, setIsInitCRUD] = useRecoilState(
  //   mysqlCompatibelCRUDInitState
  // );
  const { enqueueSnackbar } = useSnackbar();

  const session = useRecoilValue(sessionState);
  const [isInitCRUD] = useInitCRUD((error) => {
    enqueueSnackbar(`Error[init simple CRUD]: ${error?.message}`, {
      variant: "error",
    });
    setGlobalLoading(false);
  }, !!session);

  React.useEffect(() => {
    let sql = SQL_CONTENT;
    if (pdName) {
      sql = sql.replace("${NAME}", pdName);
    }
    if (price) {
      sql = sql.replace("${PRICE}", price.toString());
    }
    if (brand) {
      sql = sql.replace("${BRAND_ID}", brand);
    }
    if (stock) {
      sql = sql.replace("${STOCK}", stock.toString());
    }
    setSqlContent(sql);
  }, [pdName, price, brand, stock]);

  // React.useEffect(() => {
  //   const init = async () => {
  //     try {
  //       const data = await initCRUD();
  //       console.log(`initcrud`, data);
  //       setIsInitCRUD(true);
  //     } catch (error: any) {
  //       console.error(error);
  //       enqueueSnackbar(`Error[init simple CRUD]: ${error?.message}`, {
  //         variant: "error",
  //       });
  //       setGlobalLoading(false);
  //     } finally {
  //     }
  //   };

  //   !isInitCRUD && init();
  // }, [isInitCRUD]);

  React.useEffect(() => {
    const firstLoad = async () => {
      try {
        const data = await retrieveBrandList();
        const originalData = await retrieveProductList();
        if (data?.rows?.length > 0) {
          setBrandList(data.rows);
        }
        if (originalData?.rows?.length > 0) {
          setOriginalResult(originalData);
        }
      } catch (error: any) {
        console.error(error);
        enqueueSnackbar(`Error[fetch brand list]: ${error?.message}`, {
          variant: "error",
        });
      } finally {
        setGlobalLoading(false);
      }
    };

    isInitCRUD && firstLoad();
  }, [isInitCRUD, session]);

  React.useEffect(() => {
    if (!brand && brandList.length > 0) {
      setBrand(brandList[0].brandId);
    }
  }, [brand, brandList]);

  const handleEditName = (event: React.ChangeEvent<HTMLInputElement>) => {
    const value = event.target.value.trim();
    if (value.length === 0) {
      setPdNameError("Product name cannot be empty");
      setPdName(null);
    } else {
      setPdNameError(null);
      setPdName(value);
    }
  };

  const handleEditPrice = (event: React.ChangeEvent<HTMLInputElement>) => {
    const value = event.target.value.trim();
    if (value.length === 0) {
      setPriceError("Price cannot be empty");
      setPrice(null);
    } else {
      const price = Number(value);
      if (isNaN(price)) {
        setPriceError("Price must be a number");
        setPrice(null);
      } else if (price <= 0 || price > 1000000) {
        setPriceError("Price must be a positive number");
        setPrice(null);
      } else {
        setPriceError(null);
        setPrice(price);
      }
    }
  };

  const handleEditStock = (event: React.ChangeEvent<HTMLInputElement>) => {
    const value = event.target.value.trim();
    if (value.length === 0) {
      setStockError("Stock cannot be empty");
      setStock(null);
    } else {
      const stock = Number(value);
      if (isNaN(stock)) {
        setStockError("Stock must be a number");
        setStock(null);
      } else if (stock <= 0 || stock > 1000000 || Math.floor(stock) !== stock) {
        setStockError("Stock must be a positive INT number");
        setStock(null);
      } else {
        setStockError(null);
        setStock(stock);
      }
    }
  };

  const handleSubmmit = async () => {
    if (!pdName || !price || !brand || !stock) {
      return;
    }
    setSubmitting(true);
    setResult(null);
    setPostResult(null);
    try {
      const postaData = await createProduct({
        productName: pdName,
        price: price,
        brandId: brand,
        stock: stock,
      });
      setPostResult(postaData);
      const data = await retrieveProductList();
      setResult(data);
    } catch (error: any) {
      console.error(error);
      enqueueSnackbar(`Error[create product]: ${error?.message}`, {
        variant: "error",
      });
    } finally {
      setSubmitting(false);
    }
  };

  const tableFieldsMemo = React.useMemo(() => {
    // if (result?.fields) {
    //   return result.fields.map((field) => field.name);
    // } else if (originalResult?.fields) {
    //   return originalResult.fields.map((field) => field.name);
    // }
    return ["productId", "productName", "price", "stock", "brandId"];
  }, [result, originalResult]);

  const tableRowsMemo = React.useMemo(() => {
    const originalResultRowsIdList =
      originalResult?.rows?.map((i) => i[tableFieldsMemo[0] || "productId"]) ||
      [];
    if (result?.rows) {
      const resultRowsIdList = result.rows.map((row) => {
        !originalResultRowsIdList.includes(
          row[tableFieldsMemo[0] || "productId"]
        ) && (row._add = true);
        return row;
      });
      return resultRowsIdList;
    } else if (originalResult?.rows) {
      return originalResult.rows;
    }
    return [];
  }, [result, originalResult]);

  return (
    <>
      <ActionTemplate
        inputTitle="Add a new product:"
        sqlContent={sqlContent}
        submitting={submitting || globalLoading}
        disableSubbmit={!pdName || !price || !brand || !stock || globalLoading}
        handleSubmmit={handleSubmmit}
        result={postResult && `OK, affects 1 row (s).`}
        submitBtnLabel={
          globalLoading ? "Init sample data" : "Run SQL and Insert"
        }
      >
        <TextFieldWrapper title="Product Name">
          <TextField
            id="product-name"
            size="small"
            InputProps={{
              endAdornment: (
                <EditIcon
                  sx={{
                    color: "rgba(168, 177, 193, 0.3)",
                  }}
                />
              ),
            }}
            onChange={handleEditName}
            error={!!pdNameError}
            helperText={pdNameError}
          />
        </TextFieldWrapper>
        <TextFieldWrapper title="Price($)">
          <TextField
            id="price"
            size="small"
            InputProps={{
              endAdornment: (
                <EditIcon
                  sx={{
                    color: "rgba(168, 177, 193, 0.3)",
                  }}
                />
              ),
            }}
            onChange={handleEditPrice}
            error={!!priceError}
            helperText={priceError}
          />
        </TextFieldWrapper>
        <TextFieldWrapper title="Brand">
          <Select
            size="small"
            id="brand"
            value={brand}
            onChange={(event) => {
              const value = event.target.value;
              setBrand(value as string);
            }}
            sx={{
              width: "239px",
            }}
          >
            {globalLoading && (
              <MenuItem value="">
                <CircularProgress size={20} />
                <em>Loading...</em>
              </MenuItem>
            )}
            {brandList.map((brand) => (
              <MenuItem key={brand.brandId} value={brand.brandId}>
                {brand.brandName}({brand.brandId})
              </MenuItem>
            ))}
          </Select>
        </TextFieldWrapper>
        <TextFieldWrapper title="Stock">
          <TextField
            id="stock"
            size="small"
            InputProps={{
              endAdornment: (
                <EditIcon
                  sx={{
                    color: "rgba(168, 177, 193, 0.3)",
                  }}
                />
              ),
            }}
            onChange={handleEditStock}
            error={!!stockError}
            helperText={stockError}
          />
        </TextFieldWrapper>
      </ActionTemplate>

      <ResultPreviewTemplate>
        <ResultTable fields={tableFieldsMemo} rows={tableRowsMemo} />
      </ResultPreviewTemplate>
    </>
  );
}

export function TextFieldWrapper(props: {
  children: React.ReactNode;
  title: string;
  titleSx?: TypographyProps["sx"];
}) {
  const { children, title, titleSx } = props;

  return (
    <Box
      sx={{
        display: "flex",
        alignItems: "flex-start",
        justifyContent: "flex-start",
      }}
    >
      <Typography
        component="div"
        fontWeight={500}
        fontSize={16}
        width={120}
        height={40}
        display="inline-flex"
        alignItems="center"
        sx={{ ...titleSx }}
      >
        {title}
      </Typography>
      {children}
    </Box>
  );
}

export function ResultTable(props: {
  fields: string[];
  rows: {
    [x: string]: any;
    _add?: boolean;
    _delete?: boolean;
    _updatedIdx?: number[];
  }[];
}) {
  const { fields, rows } = props;

  const renderTableCell = (cellData: any) => {
    if (cellData === null) {
      return <Typography color="text.secondary">NULL</Typography>;
    } else if (typeof cellData === "boolean") {
      return <Typography>{cellData ? "TRUE" : "FALSE"}</Typography>;
    } else if (typeof cellData === "number") {
      return <Typography>{cellData}</Typography>;
    } else if (typeof cellData === "string") {
      return <Typography>{cellData}</Typography>;
    } else if (typeof cellData === "object") {
      // return <Typography>{JSON.stringify(cellData)}</Typography>;
      return <CodeSnippet language="json" text={JSON.stringify(cellData)} />;
    }
    return <Typography>{cellData}</Typography>;
  };

  return (
    <TableContainer
      component={Paper}
      sx={{
        maxHeight: "calc(100vh - 300px)",
      }}
    >
      <Table>
        <TableHead>
          <TableRow>
            {fields.map((field, idx) => (
              <TableCell key={field} align={idx === 0 ? "left" : "right"}>
                {field}
              </TableCell>
            ))}
          </TableRow>
        </TableHead>
        <TableBody>
          {rows.map((row, idx) => (
            <TableRow
              key={idx}
              sx={{
                "&:last-child td, &:last-child th": { border: 0 },
                ...(row?._add && {
                  backgroundColor: "#EBFFF0",
                }),
                ...(row?._delete && {
                  backgroundColor: "#FEECEC",
                  animation: "blinker 2s linear",
                  textDecoration: "line-through",

                  "@keyframes blinker": {
                    "0%": {
                      opacity: 1,
                      display: "table-row",
                    },
                    "25%": {
                      opacity: 0,
                      display: "table-row",
                    },
                    "50%": {
                      opacity: 1,
                      display: "table-row",
                    },
                    "75%": {
                      opacity: 0,
                      display: "table-row",
                    },
                    "100%": {
                      opacity: 1,
                      display: "table-row",
                    },
                  },
                }),
              }}
            >
              {fields.map((field, fieldIdx) => (
                <TableCell
                  key={`row-${idx}-${field}`}
                  align={fieldIdx === 0 ? "left" : "right"}
                >
                  {row?._updatedIdx && row._updatedIdx.includes(fieldIdx) ? (
                    <Box display="inline-flex" alignItems="center">
                      <Chip
                        size="small"
                        label="Updated"
                        sx={{
                          backgroundColor: "#CEFFDC",
                          color: "#3CDB69",
                        }}
                      />
                      {renderTableCell(row[field])}
                    </Box>
                  ) : (
                    <>{renderTableCell(row[field])}</>
                  )}
                </TableCell>
              ))}
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </TableContainer>
  );
}

export function ActionTemplate(props: {
  inputTitle: string;
  children: React.ReactNode;
  sqlContent: string;
  submitting?: boolean;
  disableSubbmit?: boolean;
  handleSubmmit: () => void;
  submitBtnLabel?: string;
  result?: string | React.ReactNode | null;
  sqlAlert?: {
    status: "warning" | "error";
    message: string | React.ReactNode;
  };
}) {
  const {
    inputTitle,
    children,
    sqlContent,
    submitting,
    disableSubbmit,
    handleSubmmit,
    submitBtnLabel,
    result,
    sqlAlert,
  } = props;

  return (
    <>
      <Grid
        xs={12}
        md={6}
        sx={{
          backgroundColor: "#fff",
        }}
      >
        <Typography component="div" gutterBottom fontWeight={500} fontSize={20}>
          {inputTitle}
        </Typography>
        <Stack spacing={1} mb={2}>
          {children}
        </Stack>
        <Box
          sx={{
            border: "1px solid #E0E0E0",
            borderRadius: "6px",
            mb: 2,
          }}
        >
          {sqlAlert && (
            <Box
              sx={{
                padding: "8px 16px",
                borderRadius: "6px 6px 0 0",
                ...(sqlAlert.status === "warning" && {
                  backgroundColor: "#FFF9E4",
                  color: "#E6A601",
                }),
              }}
            >
              <Typography fontSize={12} color="inherit">
                <WarningIcon
                  fontSize="inherit"
                  sx={{
                    mr: 0.5,
                  }}
                />
                {sqlAlert.message}
              </Typography>
            </Box>
          )}
          <CodeBlock
            language="sql"
            text={sqlContent}
            disableCopy
            boxSxProps={{
              borderRadius: "6px",
              ...(sqlAlert && { borderRadius: "0 0 6px 6px" }),
            }}
          />
        </Box>
        <LoadingButton
          variant="contained"
          loadingPosition="start"
          startIcon={<PlayArrowIcon />}
          loading={submitting}
          sx={{
            mb: 2,
          }}
          disabled={disableSubbmit}
          onClick={handleSubmmit}
        >
          {submitBtnLabel}
        </LoadingButton>
        {typeof result === "string" ? (
          <Box
            sx={{
              border: "1px solid #E0E0E0",
              borderRadius: "6px",
              minHeight: "100px",
              padding: "16px",
            }}
          >
            <Typography>{result}</Typography>
          </Box>
        ) : (
          result
        )}
      </Grid>
    </>
  );
}

export function ResultPreviewTemplate(props: { children: React.ReactNode }) {
  const { children } = props;

  return (
    <>
      <Grid
        xs={12}
        md={6}
        sx={{
          background:
            "radial-gradient(56.25% 92.01% at 50% 50.08%, #EDF6FF 10.04%, #C5E3FF 100%)",
          p: "16px 48px 48px 48px",
        }}
      >
        <Box
          display="flex"
          alignItems="center"
          justifyContent="center"
          mb="48px"
        >
          <Typography
            component="div"
            color="text.secondary"
            sx={{
              background: "#FFFFFF",
              borderRadius: "6px",
              p: "2px 8px",
              width: "fit-content",
              cursor: "default",
              userSelect: "none",
            }}
          >
            Query Result
          </Typography>
        </Box>
        <Box minHeight={200}>{children}</Box>
      </Grid>
    </>
  );
}
