import React, { useState, useCallback } from "react";
import clsx from "clsx";
import parse from "csv-parse";
import { useDropzone } from "react-dropzone";
import { useDebouncedCallback } from "use-debounce";

import { makeStyles, createStyles } from "@mui/styles";
import {
  Button,
  Popover,
  PopoverProps as MuiPopoverProps,
  Grid,
  Typography,
  TextField,
  FormHelperText,
} from "@mui/material";

import Tab from "@mui/material/Tab";
import TabContext from "@mui/lab/TabContext";
import TabList from "@mui/lab/TabList";
import TabPanel from "@mui/lab/TabPanel";

import TableHeaderButton from "./TableHeaderButton";
import ImportIcon from "@src/assets/icons/Import";

import FileUploadIcon from "@src/assets/icons/Upload";
import CheckIcon from "@mui/icons-material/CheckCircle";

import ImportCsvWizard, {
  IImportCsvWizardProps,
} from "@src/components/Wizards/ImportCsvWizard";

const useStyles = makeStyles((theme) =>
  createStyles({
    tabPanel: {
      padding: theme.spacing(2, 3),
      width: 400,
      height: 200,
    },
    continueButton: {
      margin: theme.spacing(-4, "auto", 2),
      display: "flex",
      minWidth: 100,
    },
    dropzone: {
      height: 137,
      borderRadius: theme.shape.borderRadius,
      border: `dashed 2px ${theme.palette.divider}`,
      backgroundColor: theme.palette.action.input,
      cursor: "pointer",

      "& svg": { opacity: theme.palette.action.activeOpacity },

      "&:focus": {
        borderColor: theme.palette.primary.main,
        color: theme.palette.primary.main,
        outline: "none",
      },
    },
    error: {
      "$dropzone&": {
        borderColor: theme.palette.error.main,
        color: theme.palette.error.main,
      },
    },
    dropzoneError: { margin: theme.spacing(0.5, 1.5) },

    pasteField: {
      ...theme.typography.body2,
      fontFamily: theme.typography.fontFamilyMono,
    },
    pasteInput: {
      whiteSpace: "nowrap",
      overflow: "auto",
    },
  })
);

export interface IImportCsvProps {
  render?: (
    onClick: (event: React.MouseEvent<HTMLButtonElement>) => void
  ) => React.ReactNode;
  PopoverProps?: Partial<MuiPopoverProps>;
}

export default function ImportCsv({ render, PopoverProps }: IImportCsvProps) {
  const classes = useStyles();

  const [open, setOpen] = useState<HTMLButtonElement | null>(null);
  const [tab, setTab] = useState("upload");
  const [csvData, setCsvData] =
    useState<IImportCsvWizardProps["csvData"]>(null);
  const [error, setError] = useState("");
  const validCsv =
    csvData !== null && csvData?.columns.length > 0 && csvData?.rows.length > 0;

  const handleOpen = (event: React.MouseEvent<HTMLButtonElement>) =>
    setOpen(event.currentTarget);
  const handleClose = () => {
    setOpen(null);
    setCsvData(null);
    setTab("upload");
    setError("");
  };
  const popoverId = open ? "csv-popover" : undefined;

  const parseCsv = (csvString: string) =>
    parse(csvString, {}, (err, rows) => {
      if (err) {
        setError(err.message);
      } else {
        const columns = rows.shift() ?? [];
        if (columns.length === 0) {
          setError("No columns detected");
        } else {
          const mappedRows = rows.map((row) =>
            row.reduce((a, c, i) => ({ ...a, [columns[i]]: c }), {})
          );
          setCsvData({ columns, rows: mappedRows });
          setError("");
        }
      }
    });

  const onDrop = useCallback(async (acceptedFiles) => {
    const file = acceptedFiles[0];
    const reader = new FileReader();
    reader.onload = (event: any) => parseCsv(event.target.result);
    reader.readAsText(file);
  }, []);
  const { getRootProps, getInputProps, isDragActive } = useDropzone({
    onDrop,
    multiple: false,
    accept: "text/csv",
  });

  const [handlePaste] = useDebouncedCallback(
    (value: string) => parseCsv(value),
    1000
  );

  const [loading, setLoading] = useState(false);
  const [handleUrl] = useDebouncedCallback((value: string) => {
    setLoading(true);
    setError("");
    fetch(value, { mode: "no-cors" })
      .then((res) => res.text())
      .then((data) => {
        parseCsv(data);
        setLoading(false);
      })
      .catch((e) => {
        setError(e.message);
        setLoading(false);
      });
  }, 1000);

  const [openWizard, setOpenWizard] = useState(false);

  return (
    <>
      {render ? (
        render(handleOpen)
      ) : (
        <TableHeaderButton
          title="Import CSV"
          onClick={handleOpen}
          icon={<ImportIcon />}
        />
      )}

      <Popover
        id={popoverId}
        open={!!open}
        anchorEl={open}
        onClose={handleClose}
        anchorOrigin={{
          vertical: "bottom",
          horizontal: "right",
        }}
        transformOrigin={{
          vertical: "top",
          horizontal: "right",
        }}
        {...PopoverProps}
      >
        <TabContext value={tab}>
          <TabList
            onChange={(_, v) => {
              setTab(v);
              setCsvData(null);
              setError("");
            }}
            aria-label="Import CSV method tabs"
            action={(actions) =>
              setTimeout(() => actions?.updateIndicator(), 200)
            }
            variant="fullWidth"
          >
            <Tab label="Upload" value="upload" />
            <Tab label="Paste" value="paste" />
            <Tab label="URL" value="url" />
          </TabList>

          <TabPanel value="upload" className={classes.tabPanel}>
            <Grid
              container
              justifyContent="center"
              alignContent="center"
              alignItems="center"
              direction="column"
              {...getRootProps()}
              className={clsx(classes.dropzone, error && classes.error)}
            >
              <input {...getInputProps()} />
              {isDragActive ? (
                <Typography variant="button" color="primary">
                  Drop CSV file here…
                </Typography>
              ) : (
                <>
                  <Grid item>
                    {validCsv ? <CheckIcon /> : <FileUploadIcon />}
                  </Grid>
                  <Grid item>
                    <Typography variant="button" color="inherit">
                      {validCsv
                        ? "Valid CSV"
                        : "Click to upload or drop CSV file here"}
                    </Typography>
                  </Grid>
                </>
              )}
            </Grid>

            {error && (
              <FormHelperText error className={classes.dropzoneError}>
                {error}
              </FormHelperText>
            )}
          </TabPanel>

          <TabPanel value="paste" className={classes.tabPanel}>
            <TextField
              variant="filled"
              multiline
              inputProps={{ minRows: 3 }}
              autoFocus
              fullWidth
              label="Paste CSV text"
              placeholder="column, column, …"
              onChange={(e) => {
                if (csvData !== null) setCsvData(null);
                handlePaste(e.target.value);
              }}
              InputProps={{
                classes: {
                  root: classes.pasteField,
                  input: classes.pasteInput,
                },
              }}
              helperText={error}
              error={!!error}
              sx={{ "& .MuiInputBase-input": { fontFamily: "mono" } }}
            />
          </TabPanel>

          <TabPanel value="url" className={classes.tabPanel}>
            <TextField
              variant="filled"
              autoFocus
              fullWidth
              label="Paste URL to CSV file"
              placeholder="https://"
              onChange={(e) => {
                if (csvData !== null) setCsvData(null);
                handleUrl(e.target.value);
              }}
              helperText={loading ? "Fetching CSV…" : error}
              error={!!error}
            />
          </TabPanel>
        </TabContext>

        <Button
          variant="contained"
          color="primary"
          disabled={!validCsv}
          className={classes.continueButton}
          onClick={() => setOpenWizard(true)}
        >
          Continue
        </Button>
      </Popover>

      {openWizard && csvData && (
        <ImportCsvWizard
          handleClose={() => setOpenWizard(false)}
          csvData={csvData}
        />
      )}
    </>
  );
}
