import { DateSelectArg, EventClickArg } from "@fullcalendar/core";
import frLocale from "@fullcalendar/core/locales/fr";
import dayGridPlugin from "@fullcalendar/daygrid";
import interactionPlugin from "@fullcalendar/interaction";
import listPlugin from "@fullcalendar/list";
import FullCalendar from "@fullcalendar/react";
import DeleteIcon from "@mui/icons-material/Delete";
import EditIcon from "@mui/icons-material/Edit";
import RuleIcon from "@mui/icons-material/Rule";
import {
  Alert,
  Avatar,
  Card,
  CardActions,
  CardContent,
  CardHeader,
  Checkbox,
  FormControl,
  FormControlLabel,
  FormGroup,
  FormLabel,
  IconButton,
  Menu,
  MenuItem,
  Popover,
  Tooltip,
  Typography,
} from "@mui/material";
import { red } from "@mui/material/colors";
import React from "react";
import { FamilyApi, FamilyUser } from "../../../api/FamilyApi";
import { Accommodation } from "../../../api/accommodations/AccommodationListApi";
import {
  AccommodationReservation,
  AccommodationsReservationsApi,
  AccommodationsReservationsList,
  ValidateResaResult,
} from "../../../api/accommodations/AccommodationsReservationsApi";
import { useAlert } from "../../../hooks/context_providers/AlertDialogProvider";
import { useConfirm } from "../../../hooks/context_providers/ConfirmDialogProvider";
import { useLoadingMessage } from "../../../hooks/context_providers/LoadingMessageProvider";
import { useSnackbar } from "../../../hooks/context_providers/SnackbarProvider";
import { useUpdateAccommodationReservation } from "../../../hooks/context_providers/accommodations/UpdateReservationDialogProvider";
import {
  fmtUnixDate,
  fmtUnixDateFullCalendar,
} from "../../../utils/time_utils";
import { AsyncWidget } from "../../../widgets/AsyncWidget";
import { useUser } from "../../../widgets/BaseAuthenticatedPage";
import { useFamily } from "../../../widgets/BaseFamilyRoute";
import { FamilyPageTitle } from "../../../widgets/FamilyPageTitle";
import { useAccommodations } from "../../../widgets/accommodations/BaseAccommodationsRoute";

export function AccommodationsReservationsRoute(): React.ReactElement {
  const snackbar = useSnackbar();
  const alert = useAlert();
  const confirm = useConfirm();
  const loadingMessage = useLoadingMessage();

  const loadKey = React.useRef(1);

  const user = useUser();
  const family = useFamily();
  const accommodations = useAccommodations();
  const updateReservation = useUpdateAccommodationReservation();

  const [reservations, setReservations] = React.useState<
    AccommodationsReservationsList | undefined
  >();
  const [users, setUsers] = React.useState<FamilyUser[] | null>(null);

  const [showValidated, setShowValidated] = React.useState(true);
  const [showRejected, setShowRejected] = React.useState(true);
  const [showPending, setShowPending] = React.useState(true);

  const [hiddenPeople, setHiddenPeople] = React.useState<Set<number>>(
    new Set()
  );
  const [hiddenAccommodations, setHiddenAccommodations] = React.useState<
    Set<number>
  >(new Set());

  const eventPopupAnchor = React.useRef<HTMLDivElement>(null);
  const [activeEvent, setActiveEvent] = React.useState<
    | undefined
    | {
        user: FamilyUser;
        accommodation: Accommodation;
        reservation: AccommodationReservation;

        x: number;
        y: number;
        w: number;
        h: number;
      }
  >();

  const [validateResaAnchorEl, setValidateResaAnchorEl] =
    React.useState<null | HTMLElement>(null);

  const load = async () => {
    setReservations(
      await AccommodationsReservationsApi.FullListOfFamily(family.family)
    );
    setUsers(await FamilyApi.GetUsersList(family.family.family_id));
  };

  const reload = async () => {
    loadKey.current += 1;
    setUsers(null);
  };

  const visibleReservations = React.useMemo(() => {
    return reservations?.filter((r) => {
      if (!showValidated && r.validated === true) return false;
      if (!showPending && r.validated === null) return false;
      if (!showRejected && r.validated === false) return false;
      if (hiddenPeople.has(r.user_id)) return false;
      if (hiddenAccommodations.has(r.accommodation_id)) return false;
      return true;
    });
  }, [
    showValidated,
    showRejected,
    showPending,
    hiddenPeople,
    hiddenAccommodations,
    reservations,
  ]);

  const onSelect = async (d: DateSelectArg) => {
    try {
      const resa = await updateReservation(
        {
          accommodation_id: -1,
          start: Math.floor(d.start.getTime() / 1000),
          end: Math.floor(d.end.getTime() / 1000),
        },
        true
      );

      if (!resa) return;

      loadingMessage.show("Création de la réservation en cours...");

      await AccommodationsReservationsApi.Create(family.family, resa);

      reload();
      snackbar("La réservation a été créée avec succès !");
    } catch (e) {
      console.error("Failed to create a reservation!", e);
      alert("Échec de la création de la réservation!");
    } finally {
      loadingMessage.hide();
    }
  };

  const onEventClick = (ev: EventClickArg) => {
    const id: number = ev.event.extendedProps.id;
    const resa = reservations?.get(id)!;
    const acc = accommodations.accommodations.get(resa.accommodation_id)!;

    const user = users?.find((u) => u.user_id === resa.user_id);

    if (!user) {
      console.error(`User ${resa.user_id} not found!`);
      return;
    }

    const loc = ev.el.getBoundingClientRect();
    setActiveEvent({
      reservation: resa,
      accommodation: acc,
      user: user,

      x: loc.left,
      y: loc.top,
      w: loc.width,
      h: loc.height,
    });
  };

  const respondToResaRequest = async (
    r: AccommodationReservation,
    validate: boolean
  ) => {
    try {
      loadingMessage.show("Validation de la réservation en cours...");

      setValidateResaAnchorEl(null);
      setActiveEvent(undefined);

      const res = await AccommodationsReservationsApi.Validate(r, validate);

      if (res === ValidateResaResult.Conflict) {
        throw new Error(
          "The reservation is in conflict with other reservations!"
        );
      } else if (res === ValidateResaResult.Error) {
        throw new Error("Failed to validate the reservation!");
      }

      reload();
      snackbar("La réservation a été mise à jour avec succès !");
    } catch (e) {
      console.error("Failed to respond to reservation request!", e);
      alert(`Echec de l'enregistrement de la réponse à la réservation ! ${e}`);
    } finally {
      loadingMessage.hide();
    }
  };

  const validateReservation = async (r: AccommodationReservation) => {
    respondToResaRequest(r, true);
  };

  const rejectReservation = async (r: AccommodationReservation) => {
    if (
      !(await confirm(
        "Voulez-vous vraiment rejeter cette demande de réservation ?"
      ))
    )
      return;
    respondToResaRequest(r, false);
  };

  const changeReservation = async (r: AccommodationReservation) => {
    try {
      const ac = accommodations.accommodations.get(r.accommodation_id);
      if (
        ac?.need_validation &&
        !(await confirm(
          "Voulez-vous vraiment changer cette réservation ? Celle-ci devra être de nouveau validée !"
        ))
      )
        return;

      const newResa = await updateReservation(
        {
          reservation_id: r.id,
          accommodation_id: r.accommodation_id,
          start: r.reservation_start,
          end: r.reservation_end,
        },
        false
      );

      if (!newResa) return;

      setActiveEvent(undefined);
      loadingMessage.show("Mise à jour de la réservation en cours...");

      await AccommodationsReservationsApi.Update(family.family, newResa);

      reload();
      snackbar("La réservation a été mise à jour avec succès !");
    } catch (e) {
      console.error("Failed to update a reservation!", e);
      alert("Échec de la mise à jour de la réservation!");
    } finally {
      loadingMessage.hide();
    }
  };

  const deleteReservation = async (r: AccommodationReservation) => {
    try {
      if (
        !(await confirm(
          "Voulez-vous vraiment supprimer cette réservation ? L'opération n'est pas réversible !"
        ))
      )
        return;

      setActiveEvent(undefined);
      loadingMessage.show("Suppression de la réservation en cours...");

      await AccommodationsReservationsApi.Delete(r);

      reload();
      snackbar("La réservation a été supprimée avec succès !");
    } catch (e) {
      console.error("Failed to delete a reservation!", e);
      alert("Échec de la suppression de la réservation!");
    } finally {
      loadingMessage.hide();
    }
  };

  return (
    <>
      <FamilyPageTitle title="Réservations" />
      <AsyncWidget
        loadKey={loadKey.current}
        load={load}
        errMsg="Echec du chargement de la liste des réservations !"
        build={() => (
          <div style={{ display: "flex", flexDirection: "row" }}>
            <div style={{ flex: 1, maxWidth: "250px", marginRight: "20px" }}>
              <Alert severity="info">
                Cliquez sur le calendrier pour créer une réservation.
              </Alert>

              {/* Invitation status */}
              <FormControl
                sx={{ m: 3 }}
                component="fieldset"
                variant="standard"
              >
                <FormLabel component="legend">Status</FormLabel>
                <FormGroup>
                  <FormControlLabel
                    control={
                      <Checkbox
                        checked={showValidated}
                        onChange={(_ev, v) => setShowValidated(v)}
                        color="success"
                      />
                    }
                    label="Validées"
                  />
                  <FormControlLabel
                    control={
                      <Checkbox
                        checked={showRejected}
                        onChange={(_ev, v) => setShowRejected(v)}
                        color="error"
                      />
                    }
                    label="Rejetées"
                  />
                  <FormControlLabel
                    control={
                      <Checkbox
                        checked={showPending}
                        onChange={(_ev, v) => setShowPending(v)}
                        color="info"
                      />
                    }
                    label="En attente de validation"
                  />
                </FormGroup>
              </FormControl>

              {/* Accommodations */}
              <FormControl
                sx={{ m: 3 }}
                component="fieldset"
                variant="standard"
              >
                <FormLabel component="legend">Logements</FormLabel>
                <FormGroup>
                  {accommodations.accommodations.fullList.map((a) => (
                    <FormControlLabel
                      key={a.id}
                      control={
                        <Checkbox
                          sx={{
                            color: "#" + a.color,
                            "&.Mui-checked": {
                              color: "#" + a.color,
                            },
                          }}
                          checked={!hiddenAccommodations.has(a.id)}
                          onChange={(_ev, v) => {
                            if (v) hiddenAccommodations.delete(a.id);
                            else hiddenAccommodations.add(a.id);
                            setHiddenAccommodations(
                              new Set(hiddenAccommodations)
                            );
                          }}
                        />
                      }
                      label={a.name}
                    />
                  ))}
                </FormGroup>
              </FormControl>

              {/* People */}
              <FormControl
                sx={{ m: 3 }}
                component="fieldset"
                variant="standard"
              >
                <FormLabel component="legend">Personnes</FormLabel>
                <FormGroup>
                  {users?.map((u) => (
                    <FormControlLabel
                      key={u.user_id}
                      control={
                        <Checkbox
                          checked={!hiddenPeople.has(u.user_id)}
                          onChange={(_ev, v) => {
                            if (v) hiddenPeople.delete(u.user_id);
                            else hiddenPeople.add(u.user_id);
                            setHiddenPeople(new Set(hiddenPeople));
                          }}
                        />
                      }
                      label={u.user_name}
                    />
                  ))}
                </FormGroup>
              </FormControl>
            </div>

            {/* The calendar */}
            <div style={{ flex: 5 }}>
              <FullCalendar
                editable={true}
                selectable={true}
                plugins={[dayGridPlugin, listPlugin, interactionPlugin]}
                initialView="dayGridMonth"
                height="700px"
                locale={frLocale}
                headerToolbar={{
                  left: "prev,next today",
                  center: "title",
                  right: "dayGridMonth,dayGridWeek,dayGridDay,listWeek",
                }}
                select={onSelect}
                eventClick={onEventClick}
                events={visibleReservations?.map((r) => {
                  const a = accommodations.accommodations.get(
                    r.accommodation_id
                  )!;
                  const u = users?.find((u) => u.user_id === r.user_id);
                  return {
                    title: `${u?.user_name} - ${a.name}`,
                    start: fmtUnixDateFullCalendar(r.reservation_start, false),
                    end: fmtUnixDateFullCalendar(r.reservation_end, true),
                    allDay: true,
                    color: a.color ? "#" + a.color : undefined,
                    borderColor:
                      r.validated === true
                        ? "green"
                        : r.validated === false
                        ? "red"
                        : "grey ",
                    extendedProps: {
                      id: r.id,
                    },
                  };
                })}
              />
            </div>

            {/* Calendar event popover */}
            <div
              ref={eventPopupAnchor}
              id="active-event-anchor"
              style={{
                position: "fixed",
                top: activeEvent?.y + "px",
                left: activeEvent?.x + "px",
                width: activeEvent?.w + "px",
                height: activeEvent?.h + "px",
                backgroundColor: "pink",
                zIndex: 0,
              }}
            ></div>
            <Popover
              open={activeEvent !== undefined}
              anchorEl={eventPopupAnchor.current}
              onClose={() => {
                setActiveEvent(undefined);
              }}
              anchorOrigin={{
                vertical: "bottom",
                horizontal: "left",
              }}
            >
              <Card sx={{ maxWidth: 345 }} elevation={6}>
                <CardHeader
                  avatar={
                    <Avatar sx={{ bgcolor: red[500] }}>
                      {activeEvent?.user.user_name
                        .substring(0, 1)
                        .toLocaleUpperCase()}
                    </Avatar>
                  }
                  title={activeEvent?.user.user_name}
                  subheader={activeEvent?.user.user_mail}
                />

                <CardContent>
                  <Typography variant="body2" color="text.secondary">
                    <p>
                      Réservation de {activeEvent?.accommodation.name}
                      <br />
                      <em>{activeEvent?.accommodation.description}</em>
                    </p>
                    <p>
                      Du{" "}
                      {fmtUnixDate(
                        activeEvent?.reservation.reservation_start ?? 0
                      )}{" "}
                      <br />
                      Au{" "}
                      {fmtUnixDate(
                        activeEvent?.reservation.reservation_end ?? 0
                      )}
                    </p>
                    <p>
                      <strong>
                        {activeEvent?.reservation.validated === false ? (
                          <span style={{ color: "#f44336" }}>Refusée</span>
                        ) : activeEvent?.reservation.validated === true ? (
                          <span style={{ color: "#66bb6a" }}>Validée</span>
                        ) : (
                          <span style={{ color: "#29b6f6" }}>
                            En attente de validation
                          </span>
                        )}
                      </strong>
                    </p>
                  </Typography>
                </CardContent>
                <CardActions disableSpacing>
                  {activeEvent?.accommodation.need_validation &&
                    family.family.is_admin && (
                      <>
                        <Tooltip
                          title="Valider (ou rejeter) la réservation"
                          arrow
                        >
                          <IconButton
                            onClick={(e) =>
                              setValidateResaAnchorEl(e.currentTarget)
                            }
                          >
                            <RuleIcon />
                          </IconButton>
                        </Tooltip>
                        <Menu
                          anchorEl={validateResaAnchorEl}
                          open={!!validateResaAnchorEl && !!activeEvent}
                          onClose={() => setValidateResaAnchorEl(null)}
                        >
                          <MenuItem
                            disabled={
                              activeEvent.reservation.validated === true
                            }
                            onClick={() =>
                              validateReservation(activeEvent.reservation)
                            }
                          >
                            Valider
                          </MenuItem>
                          <MenuItem
                            disabled={
                              activeEvent.reservation.validated === false
                            }
                            onClick={() =>
                              rejectReservation(activeEvent.reservation)
                            }
                          >
                            Rejeter
                          </MenuItem>
                        </Menu>
                      </>
                    )}

                  {user.user.id === activeEvent?.reservation.user_id && (
                    <>
                      <Tooltip title="Modifier les dates de réservation" arrow>
                        <IconButton
                          disabled={
                            !activeEvent.accommodation.open_to_reservations
                          }
                          onClick={() =>
                            changeReservation(activeEvent?.reservation)
                          }
                        >
                          <EditIcon />
                        </IconButton>
                      </Tooltip>
                      <Tooltip title="Supprimer la réservation" arrow>
                        <IconButton
                          color="error"
                          onClick={() =>
                            deleteReservation(activeEvent?.reservation)
                          }
                        >
                          <DeleteIcon />
                        </IconButton>
                      </Tooltip>
                    </>
                  )}
                </CardActions>
              </Card>
            </Popover>
          </div>
        )}
      />
    </>
  );
}
