import { AnimatePresence, m } from "framer-motion";
import { Trans, useTranslation } from "next-i18next";
import * as React from "react";
import { useMeasure } from "react-use";

import ArrowLeft from "@/components/icons/arrow-left.svg";
import ArrowRight from "@/components/icons/arrow-right.svg";

import { Button } from "../button";
import { useNewParticipantModal } from "../new-participant-modal";
import { useParticipants } from "../participants-provider";
import { usePoll } from "../poll-context";
import TimeZonePicker from "../time-zone-picker";
import ParticipantRow from "./desktop-poll/participant-row";
import ParticipantRowForm from "./desktop-poll/participant-row-form";
import { PollContext } from "./desktop-poll/poll-context";
import PollHeader from "./desktop-poll/poll-header";
import {
  useAddParticipantMutation,
  useUpdateParticipantMutation,
} from "./mutations";

const minSidebarWidth = 200;

const Poll: React.VoidFunctionComponent = () => {
  const { t } = useTranslation("app");

  const { poll, options, targetTimeZone, setTargetTimeZone, userAlreadyVoted } =
    usePoll();

  const { participants } = useParticipants();

  const [ref, { width }] = useMeasure<HTMLDivElement>();
  const [editingParticipantId, setEditingParticipantId] =
    React.useState<string | null>(null);

  const columnWidth = 80;

  const numberOfVisibleColumns = Math.min(
    options.length,
    Math.floor((width - minSidebarWidth) / columnWidth),
  );

  const sidebarWidth = Math.min(
    width - numberOfVisibleColumns * columnWidth,
    275,
  );

  const availableSpace = Math.min(
    numberOfVisibleColumns * columnWidth,
    options.length * columnWidth,
  );

  const [activeOptionId, setActiveOptionId] =
    React.useState<string | null>(null);

  const [scrollPosition, setScrollPosition] = React.useState(0);

  const maxScrollPosition =
    columnWidth * options.length - columnWidth * numberOfVisibleColumns;

  const [shouldShowNewParticipantForm, setShouldShowNewParticipantForm] =
    React.useState(!poll.closed && !userAlreadyVoted);

  const pollWidth = sidebarWidth + options.length * columnWidth;

  const addParticipant = useAddParticipantMutation();

  const goToNextPage = () => {
    setScrollPosition(
      Math.min(
        maxScrollPosition,
        scrollPosition + numberOfVisibleColumns * columnWidth,
      ),
    );
  };

  const goToPreviousPage = () => {
    setScrollPosition(
      Math.max(0, scrollPosition - numberOfVisibleColumns * columnWidth),
    );
  };

  const updateParticipant = useUpdateParticipantMutation();

  const showNewParticipantModal = useNewParticipantModal();
  return (
    <PollContext.Provider
      value={{
        activeOptionId,
        setActiveOptionId,
        scrollPosition,
        setScrollPosition,
        columnWidth,
        sidebarWidth,
        goToNextPage,
        goToPreviousPage,
        numberOfColumns: numberOfVisibleColumns,
        availableSpace,
        maxScrollPosition,
      }}
    >
      <div
        className="relative min-w-full max-w-full" // Don't add styles like border, margin, padding – that can mess up the sizing calculations
        style={{ width: pollWidth }}
        ref={ref}
      >
        <div className="flex flex-col overflow-hidden">
          <div className="flex h-14 shrink-0 items-center justify-between border-b bg-gradient-to-b from-gray-50 to-gray-100/50 p-3">
            <div className="p-1">
              {shouldShowNewParticipantForm || editingParticipantId ? (
                <Trans
                  t={t}
                  i18nKey="saveInstruction"
                  values={{
                    action: shouldShowNewParticipantForm
                      ? t("continue")
                      : t("save"),
                  }}
                  components={{ b: <strong /> }}
                />
              ) : (
                <div className="flex gap-2">
                  <div className="font-semibold text-slate-800">
                    {t("participantCount", { count: participants.length })}
                  </div>
                  {poll.closed ? null : (
                    <button
                      className="rounded hover:text-primary-500"
                      onClick={() => {
                        setEditingParticipantId(null);
                        setShouldShowNewParticipantForm(true);
                      }}
                    >
                      + {t("new")}
                    </button>
                  )}
                </div>
              )}
            </div>
            <div className="flex items-center gap-3">
              <div className="p-1">
                {t("optionCount", { count: options.length })}
              </div>
              {maxScrollPosition > 0 ? (
                <div className="flex gap-2">
                  <Button
                    onClick={goToPreviousPage}
                    disabled={scrollPosition === 0}
                  >
                    <ArrowLeft className="h-4 w-4" />
                  </Button>
                  <Button
                    className="text-xs"
                    disabled={scrollPosition === maxScrollPosition}
                    onClick={() => {
                      goToNextPage();
                    }}
                  >
                    <ArrowRight className="h-4 w-4" />
                  </Button>
                </div>
              ) : null}
            </div>
          </div>
          {poll.timeZone ? (
            <div className="flex h-14 shrink-0 items-center justify-end space-x-4 border-b bg-gradient-to-b from-gray-50 to-gray-100/50 px-4">
              <div className="flex grow items-center">
                <div className="mr-2 text-sm font-medium text-slate-500">
                  {t("timeZone")}
                </div>
                <TimeZonePicker
                  value={targetTimeZone}
                  onChange={setTargetTimeZone}
                  className="grow"
                />
              </div>
            </div>
          ) : null}
          <div>
            <div className="flex py-3">
              <div
                className="flex shrink-0 items-center pl-4 pr-2 font-medium"
                style={{ width: sidebarWidth }}
              ></div>
              <PollHeader />
            </div>
          </div>
          <div className="pb-2">
            <AnimatePresence initial={false}>
              {shouldShowNewParticipantForm &&
              !poll.closed &&
              !editingParticipantId ? (
                <m.div
                  variants={{
                    hidden: { height: 0, y: -50, opacity: 0 },
                    visible: { height: "auto", y: 0, opacity: 1 },
                    exit: { height: 0, opacity: 0, y: -25 },
                  }}
                  initial="hidden"
                  animate="visible"
                >
                  <ParticipantRowForm
                    className="shrink-0"
                    onSubmit={async ({ votes }) => {
                      showNewParticipantModal({
                        votes,
                        onSubmit: () => {
                          setShouldShowNewParticipantForm(false);
                        },
                      });
                    }}
                  />
                </m.div>
              ) : null}
            </AnimatePresence>
            {participants.map((participant, i) => {
              return (
                <ParticipantRow
                  key={i}
                  className={
                    editingParticipantId &&
                    editingParticipantId !== participant.id
                      ? "opacity-50"
                      : ""
                  }
                  participant={participant}
                  disableEditing={!!editingParticipantId}
                  editMode={editingParticipantId === participant.id}
                  onChangeEditMode={(isEditing) => {
                    if (isEditing) {
                      setShouldShowNewParticipantForm(false);
                      setEditingParticipantId(participant.id);
                    }
                  }}
                  onSubmit={async ({ votes }) => {
                    await updateParticipant.mutateAsync({
                      participantId: participant.id,
                      pollId: poll.id,
                      votes,
                    });
                    setEditingParticipantId(null);
                  }}
                />
              );
            })}
          </div>
          <AnimatePresence initial={false}>
            {shouldShowNewParticipantForm || editingParticipantId ? (
              <m.div
                variants={{
                  hidden: { height: 0, y: 30, opacity: 0 },
                  visible: { height: "auto", y: 0, opacity: 1 },
                }}
                initial="hidden"
                animate="visible"
                exit="hidden"
                className="flex shrink-0 items-center border-t bg-gray-50"
              >
                <div className="flex w-full items-center justify-between gap-3 p-3">
                  <Button
                    onClick={() => {
                      if (editingParticipantId) {
                        setEditingParticipantId(null);
                      } else {
                        setShouldShowNewParticipantForm(false);
                      }
                    }}
                  >
                    {t("cancel")}
                  </Button>
                  <Button
                    key="submit"
                    form="participant-row-form"
                    htmlType="submit"
                    type="primary"
                    loading={
                      addParticipant.isLoading || updateParticipant.isLoading
                    }
                  >
                    {shouldShowNewParticipantForm ? t("continue") : t("save")}
                  </Button>
                </div>
              </m.div>
            ) : null}
          </AnimatePresence>
        </div>
      </div>
    </PollContext.Provider>
  );
};

export default React.memo(Poll);
