import React, { useEffect, useState } from "react";

import { useRouter } from "next/router";

import useSWR, { mutate } from "swr";

// headless ui
import { Dialog, Transition } from "@headlessui/react";
// services
import projectService from "services/project.service";
import modulesService from "services/modules.service";
import issuesService from "services/issues.service";
// hooks
import useUser from "hooks/use-user";
import useToast from "hooks/use-toast";
// components
import { IssueForm } from "components/issues";
// types
import type { IIssue } from "types";
// fetch keys
import {
  PROJECT_ISSUES_DETAILS,
  PROJECT_ISSUES_LIST,
  CYCLE_ISSUES,
  USER_ISSUE,
  PROJECTS_LIST,
  MODULE_ISSUES,
  SUB_ISSUES,
} from "constants/fetch-keys";

export interface IssuesModalProps {
  isOpen: boolean;
  handleClose: () => void;
  data?: IIssue | null;
  prePopulateData?: Partial<IIssue>;
  isUpdatingSingleIssue?: boolean;
}

export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
  isOpen,
  handleClose,
  data,
  prePopulateData,
  isUpdatingSingleIssue = false,
}) => {
  // states
  const [createMore, setCreateMore] = useState(false);
  const [activeProject, setActiveProject] = useState<string | null>(null);

  const router = useRouter();
  const { workspaceSlug, projectId, cycleId, moduleId } = router.query;

  if (cycleId) prePopulateData = { ...prePopulateData, cycle: cycleId as string };
  if (moduleId) prePopulateData = { ...prePopulateData, module: moduleId as string };

  const { user } = useUser();
  const { setToastAlert } = useToast();

  const { data: issues } = useSWR(
    workspaceSlug && activeProject
      ? PROJECT_ISSUES_LIST(workspaceSlug as string, activeProject ?? "")
      : null,
    workspaceSlug && activeProject
      ? () => issuesService.getIssues(workspaceSlug as string, activeProject ?? "")
      : null
  );

  const { data: projects } = useSWR(
    workspaceSlug ? PROJECTS_LIST(workspaceSlug as string) : null,
    workspaceSlug ? () => projectService.getProjects(workspaceSlug as string) : null
  );

  useEffect(() => {
    if (projects && projects.length > 0)
      setActiveProject(projects?.find((p) => p.id === projectId)?.id ?? projects?.[0].id ?? null);
  }, [projectId, projects]);

  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.key === "Escape") {
        handleClose();
      }
    };

    window.addEventListener("keydown", handleKeyDown);
    return () => {
      window.removeEventListener("keydown", handleKeyDown);
    };
  }, [handleClose]);

  const addIssueToCycle = async (issueId: string, cycleId: string) => {
    if (!workspaceSlug || !projectId) return;

    await issuesService
      .addIssueToCycle(workspaceSlug as string, activeProject ?? "", cycleId, {
        issues: [issueId],
      })
      .then((res) => {
        mutate(CYCLE_ISSUES(cycleId));
        if (isUpdatingSingleIssue) {
          mutate<IIssue>(
            PROJECT_ISSUES_DETAILS,
            (prevData) => ({ ...(prevData as IIssue), sprints: cycleId }),
            false
          );
        } else
          mutate<IIssue[]>(
            PROJECT_ISSUES_LIST(workspaceSlug as string, activeProject ?? ""),
            (prevData) =>
              (prevData ?? []).map((i) => {
                if (i.id === res.id) return { ...i, sprints: cycleId };
                return i;
              }),
            false
          );
      })
      .catch((err) => {
        console.log(err);
      });
  };

  const addIssueToModule = async (issueId: string, moduleId: string) => {
    if (!workspaceSlug || !projectId) return;

    await modulesService
      .addIssuesToModule(workspaceSlug as string, activeProject ?? "", moduleId as string, {
        issues: [issueId],
      })
      .then((res) => {
        console.log(res);
        mutate(MODULE_ISSUES(moduleId as string));
      })
      .catch((e) => console.log(e));
  };

  const createIssue = async (payload: Partial<IIssue>) => {
    await issuesService
      .createIssues(workspaceSlug as string, activeProject ?? "", payload)
      .then((res) => {
        mutate(PROJECT_ISSUES_LIST(workspaceSlug as string, activeProject ?? ""));

        if (payload.cycle && payload.cycle !== "") addIssueToCycle(res.id, payload.cycle);
        if (payload.module && payload.module !== "") addIssueToModule(res.id, payload.module);

        if (!createMore) handleClose();

        setToastAlert({
          type: "success",
          title: "Success!",
          message: "Issue created successfully.",
        });

        if (payload.assignees_list?.some((assignee) => assignee === user?.id)) mutate(USER_ISSUE);

        if (payload.parent && payload.parent !== "") mutate(SUB_ISSUES(payload.parent));
      })
      .catch(() => {
        setToastAlert({
          type: "error",
          title: "Error!",
          message: "Issue could not be created. Please try again.",
        });
      });
  };

  const updateIssue = async (payload: Partial<IIssue>) => {
    await issuesService
      .updateIssue(workspaceSlug as string, activeProject ?? "", data?.id ?? "", payload)
      .then((res) => {
        if (isUpdatingSingleIssue) {
          mutate<IIssue>(PROJECT_ISSUES_DETAILS, (prevData) => ({ ...prevData, ...res }), false);
        } else {
          mutate<IIssue[]>(
            PROJECT_ISSUES_LIST(workspaceSlug as string, activeProject ?? ""),
            (prevData) =>
              (prevData ?? []).map((i) => {
                if (i.id === res.id) return { ...i, ...res };
                return i;
              })
          );
        }

        if (payload.cycle && payload.cycle !== "") addIssueToCycle(res.id, payload.cycle);
        if (payload.module && payload.module !== "") addIssueToModule(res.id, payload.module);

        if (!createMore) handleClose();

        setToastAlert({
          type: "success",
          title: "Success!",
          message: "Issue updated successfully.",
        });
      })
      .catch(() => {
        setToastAlert({
          type: "error",
          title: "Error!",
          message: "Issue could not be updated. Please try again.",
        });
      });
  };

  const handleFormSubmit = async (formData: Partial<IIssue>) => {
    if (!workspaceSlug || !activeProject) return;

    const payload: Partial<IIssue> = {
      ...formData,
      assignees_list: formData.assignees,
      labels_list: formData.labels,
      description: formData.description ?? "",
      description_html: formData.description_html ?? "<p></p>",
    };

    if (!data) await createIssue(payload);
    else await updateIssue(payload);
  };

  return (
    <Transition.Root show={isOpen} as={React.Fragment}>
      <Dialog as="div" className="relative z-20" onClose={() => {}}>
        <Transition.Child
          as={React.Fragment}
          enter="ease-out duration-300"
          enterFrom="opacity-0"
          enterTo="opacity-100"
          leave="ease-in duration-200"
          leaveFrom="opacity-100"
          leaveTo="opacity-0"
        >
          <div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
        </Transition.Child>

        <div className="fixed inset-0 z-10 overflow-y-auto">
          <div className="flex min-h-full items-center justify-center p-4 text-center sm:p-0">
            <Transition.Child
              as={React.Fragment}
              enter="ease-out duration-300"
              enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
              enterTo="opacity-100 translate-y-0 sm:scale-100"
              leave="ease-in duration-200"
              leaveFrom="opacity-100 translate-y-0 sm:scale-100"
              leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
            >
              <Dialog.Panel className="relative transform rounded-lg bg-white p-5 text-left shadow-xl transition-all sm:w-full sm:max-w-2xl">
                <IssueForm
                  issues={issues ?? []}
                  handleFormSubmit={handleFormSubmit}
                  initialData={prePopulateData}
                  createMore={createMore}
                  setCreateMore={setCreateMore}
                  handleClose={handleClose}
                  projectId={activeProject ?? ""}
                  setActiveProject={setActiveProject}
                  status={data ? true : false}
                />
              </Dialog.Panel>
            </Transition.Child>
          </div>
        </div>
      </Dialog>
    </Transition.Root>
  );
};