import React, { useState } from "react";
import { EventClickArg } from "@fullcalendar/core";
import { DateClickArg } from "@fullcalendar/interaction";
import { useGlobalState } from "providers";
import { useMobile, useSnackbar, useThemeType } from "hooks";
import { useTaskAPI } from "providers";
import { useEffect, useCallback } from "react";
import TaskCalendar from "./TaskCalendar";
import TaskList from "./TaskList";
import {
  Box,
  Button,
  Fab,
  Grid2 as Grid,
  IconButton,
  LinearProgress,
  Theme,
  Typography
} from "@mui/material";
import { Task } from "models";
import TaskSettings from "./TaskSettings";
import { DayPicker } from "react-day-picker";
import "react-day-picker/dist/style.css";
import { CalendarToday } from "@mui/icons-material";
import NotesIcon from "@mui/icons-material/Notes";
import AddIcon from "@mui/icons-material/Add";
import TaskDrawer from "./TaskDrawer";
import moment from "moment";
import { makeStyles } from "@mui/styles";

interface ViewProps {
  objectKey?: string; //only used to save it for the object
  label?: string;
  drawerView?: boolean;
  loadKeys?: string[]; //if you want to load tasks for a specific object(s)
}

interface TabPanelProps {
  children?: React.ReactNode;
  index: any;
  value: any;
}

const useStyles = makeStyles((theme: Theme) => {
  const themeType = useThemeType();

  return ({
    avatar: {
      color: themeType === "light" ? theme.palette.common.black : theme.palette.common.white,
      backgroundColor: "transparent",
      width: theme.spacing(5),
      height: theme.spacing(5),
      border: "1px solid",
      borderColor: theme.palette.divider
    },
    active: {
      color: theme.palette.getContrastText(theme.palette.secondary.main),
      backgroundColor: theme.palette.secondary.main,
      width: theme.spacing(5),
      height: theme.spacing(5),
      border: 0,
      "&:hover": {
        backgroundColor: theme.palette.secondary.main
      }
    },
    fab: {
      zIndex: 20,
      background: theme.palette.primary.main,
      "&:hover": {
        background: theme.palette.primary.main
      },
      "&:focus": {
        background: theme.palette.primary.main
      },
      position: "fixed",
      bottom: theme.spacing(8), //for mobile navigator
      right: theme.spacing(2),
      [theme.breakpoints.up("sm")]: {
        bottom: theme.spacing(2)
      }
    }
  });
});

export default function TaskViewer(props: ViewProps) {
  const { objectKey, label, drawerView, loadKeys } = props;
  const [{ user }] = useGlobalState();
  const [{ as }] = useGlobalState();
  const taskAPI = useTaskAPI();
  const { openSnack } = useSnackbar();
  const [tasks, setTasks] = useState<Map<string, Task>>(new Map<string, Task>());
  const [viewTask, setViewTask] = useState<Task>(Task.create());
  const [newTaskDialog, setNewTaskDialog] = useState(false);
  const [editTask, setEditTask] = useState<Task>();
  const [loaded, setLoaded] = useState(false);
  const [currentDate, setCurrentDate] = useState(new Date());
  const isMobile = useMobile();
  const [value, setValue] = React.useState(0);
  const classes = useStyles();
  const [expand, setExpand] = useState(false);
  const [hideCal, setHideCal] = useState(false);
  const [openDrawer, setOpenDrawer] = useState(false);
  const [nextMonth, setNextMonth] = useState(
    moment(currentDate)
      .add(2, "month")
      .format("YYYY-MM")
  );
  const [prevMonth, setPrevMonth] = useState(
    moment(currentDate)
      .subtract(1, "month")
      .format("YYYY-MM")
  );

  //initial states for the task variables
  const [startDate, setStartDate] = useState<string>();

  //functions for opening and closing the newTaskDialog
  const openCalendarDialog = (args: DateClickArg) => {
    //assign the date of task when the dialog box is opened
    setStartDate(args.dateStr);
    openDialog();
  };
  const openDialog = () => {
    setNewTaskDialog(true);
  };

  //functions for opening and closing the selectedTaskDialog if selected from the calendar
  const openSelectedTask = (taskId: string) => {
    let task = tasks.get(taskId);
    if (task) {
      setOpenDrawer(true);
      setViewTask(task);
    }
  };

  const setTaskToEdit = (task: Task) => {
    setEditTask(task);
    openDialog();
  };

  const loadMultitask = useCallback(() => {
    if (!loadKeys) return;
    if (loadKeys.length > 0) {
      let temp = new Map<string, Task>();
      setLoaded(false);
      taskAPI
        .getMultiTasks(loadKeys, as)
        .then(resp => {
          resp.data.tasks.forEach(task => {
            if (task.settings) {
              temp.set(task.key, Task.any(task));
            }
          });
          setTasks(temp);
          setLoaded(true);
        })
        .catch(err => {
          openSnack("Failed to load");
        });
    }
  }, [loadKeys, openSnack, taskAPI, as]);

  //loads tasks from the backend database
  const loadTasks = useCallback(() => {
    if (!user.id()) return;
    let temp = new Map<string, Task>();
    taskAPI
      .listTasks(50, 0, "asc", "start", undefined, undefined, as, prevMonth, nextMonth)
      .then(resp => {
        resp.data.tasks.forEach(task => {
          if (task.settings) {
            temp.set(task.key, Task.any(task));
          }
        });
        setTasks(temp);
        setLoaded(true);
      })
      .catch(err => {
        openSnack("Failed to load tasks");
      });
  }, [taskAPI, as, user, openSnack, nextMonth, prevMonth]);

  useEffect(() => {
    if (drawerView) {
      setTasks(new Map<string, Task>());
      setValue(1);
      loadMultitask();
    } else {
      loadTasks();
    }
  }, [loadTasks, loadMultitask, as, drawerView]);

  const markComplete = (task: Task) => {
    task.settings.complete = !task.settings.complete;

    taskAPI
      .updateTask(task.key, task.settings, undefined, as)
      .then(resp => {
        openSnack("Task Updated");
        loadTasks();
      })
      .catch(err => {
        openSnack("Failed to update task");
      });
  };

  const deleteTask = (task: Task) => {
    taskAPI
      .removeTask(task.key, as)
      .then(resp => {
        openSnack("Task Removed");
        loadTasks();
      })
      .catch(err => {
        openSnack("Failed to remove task");
      });
  };

  const tabIcons = () => {
    return (
      <Box display="flex" justifyContent="left" alignItems="center" zIndex={5}>
        <IconButton
          className={value === 0 ? classes.active : classes.avatar}
          component="span"
          style={{
            marginBottom: "0.75rem",
            marginRight: "0.5rem"
          }}
          onClick={() => setValue(0)}>
          <CalendarToday />
        </IconButton>
        <IconButton
          className={value === 1 ? classes.active : classes.avatar}
          component="span"
          style={{
            marginBottom: "0.75rem",
            marginRight: "0.5rem"
          }}
          onClick={() => setValue(1)}>
          <NotesIcon />
        </IconButton>
      </Box>
    );
  };

  function TabPanelMine(props: TabPanelProps) {
    const { children, value, index, ...other } = props;

    return (
      <div
        role="tabpanel"
        hidden={value !== index}
        aria-labelledby={`simple-tab-${index}`}
        {...other}>
        {value === index && <React.Fragment>{children}</React.Fragment>}
      </div>
    );
  }

  const resize = () => {
    setHideCal(!hideCal);
    setExpand(!expand);
  };

  const viewer = () => {
    return (
      <React.Fragment>
        {isMobile || drawerView ? (
          <Box>
            <Fab
              onClick={openDialog}
              aria-label="Create Task"
              className={classes.fab}
              size={isMobile ? "medium" : "large"}>
              <AddIcon />
            </Fab>
            {!drawerView && tabIcons()}
            <TabPanelMine value={value} index={0}>
              <Box paddingBottom={2}>
                <TaskCalendar
                  tasks={Array.from(tasks.values())}
                  dateClickMethod={openCalendarDialog}
                  taskClickMethod={(arg: EventClickArg) => openSelectedTask(arg.event.id)}
                  centerToolbar={"hide"}
                  initialView="dayGridWeek"
                  initialDate={currentDate}
                  calHeight={"22vh"}
                  hideToday
                  hideCal={hideCal}
                  hideCalCallback={resize}
                  changeDateCallback={date => {
                    if (currentDate.getMonth() !== date.getMonth()) {
                      setCurrentDate(date);
                      setNextMonth(
                        moment(date)
                          .add(2, "month")
                          .format("YYYY-MM")
                      );
                      setPrevMonth(
                        moment(date)
                          .subtract(1, "month")
                          .format("YYYY-MM")
                      );
                      loadTasks();
                    }
                    setCurrentDate(date);
                  }}
                />
              </Box>

              <TaskCalendar
                tasks={Array.from(tasks.values())}
                dateClickMethod={openCalendarDialog}
                taskClickMethod={(arg: EventClickArg) => openSelectedTask(arg.event.id)}
                initialView="timeGridDay"
                initialDate={currentDate}
                calHeight={expand ? "59vh" : "37vh"}
                hideTitle
                changeDateCallback={date => {
                  if (currentDate.getMonth() !== date.getMonth()) {
                    setCurrentDate(date);
                    setNextMonth(
                      moment(date)
                        .add(2, "month")
                        .format("YYYY-MM")
                    );
                    setPrevMonth(
                      moment(date)
                        .subtract(1, "month")
                        .format("YYYY-MM")
                    );
                    loadTasks();
                  }
                  setCurrentDate(date);
                }}
              />
            </TabPanelMine>
            <TabPanelMine value={value} index={1}>
              <TaskList
                tasks={Array.from(tasks.values())}
                label={label}
                editTaskMethod={setTaskToEdit}
                markComplete={markComplete}
                deleteTask={deleteTask}
                reLoad={() => loadTasks()}
                dateToView={drawerView ? undefined : currentDate}
                openTask={(taskId: string) => openSelectedTask(taskId)}
              />
            </TabPanelMine>
          </Box>
        ) : (
          <Grid container direction="row" alignContent="center" alignItems="center">
            <Grid size={{lg: 3, md: 4}}>
              <Box textAlign={"center"}>
                <Button
                  onClick={openDialog}
                  style={{ margin: 5, borderRadius: 20, backgroundColor: "green", width: "50%" }}>
                  <Typography style={{ fontWeight: 750, color: "white" }}>+ Task</Typography>
                </Button>
              </Box>
              <Box
                style={{
                  width: "300px",
                  height: "280px",
                  margin: "auto"
                }}>
                <DayPicker
                  mode="single"
                  selected={currentDate}
                  onSelect={date => {
                    if (!date) return;
                    if (currentDate.getMonth() !== date.getMonth()) {
                      setCurrentDate(date);
                      setNextMonth(
                        moment(date)
                          .add(2, "month")
                          .format("YYYY-MM")
                      );
                      setPrevMonth(
                        moment(date)
                          .subtract(1, "month")
                          .format("YYYY-MM")
                      );
                      loadTasks();
                    }
                    setCurrentDate(date);
                  }}
                />
              </Box>
              <Box marginY={2}>
                <TaskList
                  tasks={Array.from(tasks.values())}
                  label={label}
                  editTaskMethod={setTaskToEdit}
                  markComplete={markComplete}
                  deleteTask={deleteTask}
                  reLoad={() => loadTasks()}
                  listHeight={"40vh"}
                  dateToView={currentDate}
                  openTask={(taskId: string) => openSelectedTask(taskId)}
                  />
              </Box>
            </Grid>
            <Grid size={{lg:9, md:8}}>
              <TaskCalendar
                tasks={Array.from(tasks.values())}
                dateClickMethod={openCalendarDialog}
                taskClickMethod={(arg: EventClickArg) => openSelectedTask(arg.event.id)}
                initialView="timeGridWeek"
                centerToolbar="dayGridMonth,timeGridWeek,timeGridDay"
                initialDate={currentDate}
                calHeight={"80vh"}
                changeDateCallback={date => {
                  if (currentDate.getMonth() !== date.getMonth()) {
                    setCurrentDate(date);
                    setNextMonth(
                      moment(date)
                        .add(2, "month")
                        .format("YYYY-MM")
                    );
                    setPrevMonth(
                      moment(date)
                        .subtract(1, "month")
                        .format("YYYY-MM")
                    );
                    loadTasks();
                  }
                  setCurrentDate(date);
                }}
              />
            </Grid>
          </Grid>
        )}
      </React.Fragment>
    );
  };

  return (
    <Box>
      {!loaded ? <LinearProgress style={{ marginTop: 20 }} /> : viewer()}
      <TaskSettings
        open={newTaskDialog}
        task={editTask}
        objectKey={objectKey}
        startDate={startDate}
        onClose={r => {
          if (r) {
            if (drawerView) {
              loadMultitask();
            } else {
              loadTasks();
            }
          }
          setEditTask(undefined);
          setNewTaskDialog(false);
        }}
      />
      {
        <TaskDrawer
          task={viewTask}
          open={openDrawer}
          closeCallback={() => setOpenDrawer(false)}
          completeTask={markComplete}
          deleteTask={deleteTask}
          editTask={setTaskToEdit}
        />
      }
    </Box>
  );
}
