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

import { useRouter } from "next/router";

import useSWR, { mutate } from "swr";

// react-hook-form
import { useForm, Controller, UseFormWatch, Control } from "react-hook-form";
// react-color
import { TwitterPicker } from "react-color";
// headless ui
import { Popover, Listbox, Transition } from "@headlessui/react";
// hooks
import useToast from "hooks/use-toast";
// services
import issuesService from "services/issues.service";
import modulesService from "services/modules.service";
// contexts
import { useProjectMyMembership } from "contexts/project-member.context";
// components
import { LinkModal, LinksList } from "components/core";
import {
  DeleteIssueModal,
  SidebarAssigneeSelect,
  SidebarBlockedSelect,
  SidebarBlockerSelect,
  SidebarCycleSelect,
  SidebarModuleSelect,
  SidebarParentSelect,
  SidebarPrioritySelect,
  SidebarStateSelect,
  SidebarEstimateSelect,
} from "components/issues";
// ui
import { Input, Spinner, CustomDatePicker } from "components/ui";
// icons
import {
  TagIcon,
  ChevronDownIcon,
  LinkIcon,
  CalendarDaysIcon,
  TrashIcon,
  PlusIcon,
  XMarkIcon,
  RectangleGroupIcon,
} from "@heroicons/react/24/outline";
// helpers
import { copyTextToClipboard } from "helpers/string.helper";
// types
import type { ICycle, IIssue, IIssueLabels, IIssueLink, IModule } from "types";
// fetch-keys
import { PROJECT_ISSUE_LABELS, PROJECT_ISSUES_LIST, ISSUE_DETAILS } from "constants/fetch-keys";

type Props = {
  control: Control<IIssue, any>;
  submitChanges: (formData: Partial<IIssue>) => void;
  issueDetail: IIssue | undefined;
  watch: UseFormWatch<IIssue>;
};

const defaultValues: Partial<IIssueLabels> = {
  name: "",
  color: "#ff0000",
};

export const IssueDetailsSidebar: React.FC<Props> = ({
  control,
  submitChanges,
  issueDetail,
  watch: watchIssue,
}) => {
  const [createLabelForm, setCreateLabelForm] = useState(false);
  const [deleteIssueModal, setDeleteIssueModal] = useState(false);
  const [linkModal, setLinkModal] = useState(false);

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

  const { memberRole } = useProjectMyMembership();

  const { setToastAlert } = useToast();

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

  const { data: issueLabels, mutate: issueLabelMutate } = useSWR<IIssueLabels[]>(
    workspaceSlug && projectId ? PROJECT_ISSUE_LABELS(projectId as string) : null,
    workspaceSlug && projectId
      ? () => issuesService.getIssueLabels(workspaceSlug as string, projectId as string)
      : null
  );

  const {
    register,
    handleSubmit,
    formState: { isSubmitting },
    reset,
    watch,
    control: controlLabel,
  } = useForm({
    defaultValues,
  });

  const handleNewLabel = (formData: any) => {
    if (!workspaceSlug || !projectId || isSubmitting) return;
    issuesService
      .createIssueLabel(workspaceSlug as string, projectId as string, formData)
      .then((res) => {
        reset(defaultValues);
        issueLabelMutate((prevData) => [...(prevData ?? []), res], false);
        submitChanges({ labels_list: [...(issueDetail?.labels ?? []), res.id] });
        setCreateLabelForm(false);
      });
  };

  const handleCycleChange = useCallback(
    (cycleDetails: ICycle) => {
      if (!workspaceSlug || !projectId || !issueDetail) return;

      issuesService
        .addIssueToCycle(workspaceSlug as string, projectId as string, cycleDetails.id, {
          issues: [issueDetail.id],
        })
        .then((res) => {
          mutate(ISSUE_DETAILS(issueId as string));
        });
    },
    [workspaceSlug, projectId, issueId, issueDetail]
  );

  const handleModuleChange = useCallback(
    (moduleDetail: IModule) => {
      if (!workspaceSlug || !projectId || !issueDetail) return;

      modulesService
        .addIssuesToModule(workspaceSlug as string, projectId as string, moduleDetail.id, {
          issues: [issueDetail.id],
        })
        .then((res) => {
          mutate(ISSUE_DETAILS(issueId as string));
        });
    },
    [workspaceSlug, projectId, issueId, issueDetail]
  );

  const handleCreateLink = async (formData: IIssueLink) => {
    if (!workspaceSlug || !projectId || !issueDetail) return;

    const payload = { metadata: {}, ...formData };

    await issuesService
      .createIssueLink(workspaceSlug as string, projectId as string, issueDetail.id, payload)
      .then(() => mutate(ISSUE_DETAILS(issueDetail.id)))
      .catch((err) => {
        if (err.status === 400)
          setToastAlert({
            type: "error",
            title: "Error!",
            message: "This URL already exists for this issue.",
          });
        else
          setToastAlert({
            type: "error",
            title: "Error!",
            message: "Something went wrong. Please try again.",
          });
      });
  };

  const handleDeleteLink = async (linkId: string) => {
    if (!workspaceSlug || !projectId || !issueDetail) return;

    const updatedLinks = issueDetail.issue_link.filter((l) => l.id !== linkId);

    mutate<IIssue>(
      ISSUE_DETAILS(issueDetail.id),
      (prevData) => ({ ...(prevData as IIssue), issue_link: updatedLinks }),
      false
    );

    await issuesService
      .deleteIssueLink(workspaceSlug as string, projectId as string, issueDetail.id, linkId)
      .then((res) => {
        mutate(ISSUE_DETAILS(issueDetail.id));
      })
      .catch((err) => {
        console.log(err);
      });
  };

  const handleCopyText = () => {
    const originURL =
      typeof window !== "undefined" && window.location.origin ? window.location.origin : "";

    copyTextToClipboard(
      `${originURL}/${workspaceSlug}/projects/${projectId}/issues/${issueDetail?.id}`
    ).then(() => {
      setToastAlert({
        type: "success",
        title: "Link Copied!",
        message: "Issue link copied to clipboard.",
      });
    });
  };

  useEffect(() => {
    if (!createLabelForm) return;

    reset();
  }, [createLabelForm, reset]);

  const isNotAllowed = memberRole.isGuest || memberRole.isViewer;

  return (
    <>
      <LinkModal
        isOpen={linkModal}
        handleClose={() => setLinkModal(false)}
        onFormSubmit={handleCreateLink}
      />
      <DeleteIssueModal
        handleClose={() => setDeleteIssueModal(false)}
        isOpen={deleteIssueModal}
        data={issueDetail ?? null}
      />
      <div className="sticky top-5 w-full divide-y-2 divide-brand-base">
        <div className="flex items-center justify-between pb-3">
          <h4 className="text-sm font-medium">
            {issueDetail?.project_detail?.identifier}-{issueDetail?.sequence_id}
          </h4>
          <div className="flex flex-wrap items-center gap-2">
            <button
              type="button"
              className="rounded-md border border-brand-base p-2 shadow-sm duration-300 hover:bg-brand-surface-1 focus:border-brand-accent focus:outline-none focus:ring-1 focus:ring-brand-accent"
              onClick={handleCopyText}
            >
              <LinkIcon className="h-3.5 w-3.5" />
            </button>
            {!isNotAllowed && (
              <button
                type="button"
                className="rounded-md border border-red-500 p-2 text-red-500 shadow-sm duration-300 hover:bg-red-500/20 focus:outline-none"
                onClick={() => setDeleteIssueModal(true)}
              >
                <TrashIcon className="h-3.5 w-3.5" />
              </button>
            )}
          </div>
        </div>
        <div className="divide-y-2 divide-brand-base">
          <div className="py-1">
            <Controller
              control={control}
              name="state"
              render={({ field: { value } }) => (
                <SidebarStateSelect
                  value={value}
                  onChange={(val: string) => submitChanges({ state: val })}
                  userAuth={memberRole}
                />
              )}
            />
            <Controller
              control={control}
              name="assignees_list"
              render={({ field: { value } }) => (
                <SidebarAssigneeSelect
                  value={value}
                  onChange={(val: string[]) => submitChanges({ assignees_list: val })}
                  userAuth={memberRole}
                />
              )}
            />
            <Controller
              control={control}
              name="priority"
              render={({ field: { value } }) => (
                <SidebarPrioritySelect
                  value={value}
                  onChange={(val: string) => submitChanges({ priority: val })}
                  userAuth={memberRole}
                />
              )}
            />
            <Controller
              control={control}
              name="estimate_point"
              render={({ field: { value } }) => (
                <SidebarEstimateSelect
                  value={value}
                  onChange={(val: number | null) => submitChanges({ estimate_point: val })}
                  userAuth={memberRole}
                />
              )}
            />
          </div>
          <div className="py-1">
            <SidebarParentSelect
              control={control}
              submitChanges={submitChanges}
              issuesList={
                issues?.filter(
                  (i) =>
                    i.id !== issueDetail?.id &&
                    i.id !== issueDetail?.parent &&
                    i.parent !== issueDetail?.id
                ) ?? []
              }
              customDisplay={
                issueDetail?.parent_detail ? (
                  <button
                    type="button"
                    className="flex items-center gap-2 rounded bg-brand-surface-2 px-3 py-2 text-xs"
                    onClick={() => submitChanges({ parent: null })}
                  >
                    {issueDetail.parent_detail?.name}
                    <XMarkIcon className="h-3 w-3" />
                  </button>
                ) : (
                  <div className="inline-block rounded bg-brand-surface-1 px-3 py-2 text-xs">
                    No parent selected
                  </div>
                )
              }
              watch={watchIssue}
              userAuth={memberRole}
            />
            <SidebarBlockerSelect
              submitChanges={submitChanges}
              issuesList={issues?.filter((i) => i.id !== issueDetail?.id) ?? []}
              watch={watchIssue}
              userAuth={memberRole}
            />
            <SidebarBlockedSelect
              submitChanges={submitChanges}
              issuesList={issues?.filter((i) => i.id !== issueDetail?.id) ?? []}
              watch={watchIssue}
              userAuth={memberRole}
            />
            <div className="flex flex-wrap items-center py-2">
              <div className="flex items-center gap-x-2 text-sm text-brand-secondary sm:basis-1/2">
                <CalendarDaysIcon className="h-4 w-4 flex-shrink-0" />
                <p>Due date</p>
              </div>
              <div className="sm:basis-1/2">
                <Controller
                  control={control}
                  name="target_date"
                  render={({ field: { value } }) => (
                    <CustomDatePicker
                      placeholder="Due date"
                      value={value}
                      onChange={(val) =>
                        submitChanges({
                          target_date: val,
                        })
                      }
                      className="bg-brand-surface-1"
                      disabled={isNotAllowed}
                    />
                  )}
                />
              </div>
            </div>
          </div>
          <div className="py-1">
            <SidebarCycleSelect
              issueDetail={issueDetail}
              handleCycleChange={handleCycleChange}
              userAuth={memberRole}
            />
            <SidebarModuleSelect
              issueDetail={issueDetail}
              handleModuleChange={handleModuleChange}
              userAuth={memberRole}
            />
          </div>
        </div>
        <div className="space-y-3 py-3">
          <div className="flex items-start justify-between">
            <div className="flex basis-1/2 items-center gap-x-2 text-sm text-brand-secondary">
              <TagIcon className="h-4 w-4" />
              <p>Label</p>
            </div>
            <div className="basis-1/2">
              <div className="flex flex-wrap gap-1">
                {watchIssue("labels_list")?.map((labelId) => {
                  const label = issueLabels?.find((l) => l.id === labelId);

                  if (label)
                    return (
                      <span
                        key={label.id}
                        className="group flex cursor-pointer items-center gap-1 rounded-2xl border border-brand-base px-1 py-0.5 text-xs hover:border-red-500/20 hover:bg-red-500/20"
                        onClick={() => {
                          const updatedLabels = watchIssue("labels_list")?.filter(
                            (l) => l !== labelId
                          );
                          submitChanges({
                            labels_list: updatedLabels,
                          });
                        }}
                      >
                        <span
                          className="h-2 w-2 flex-shrink-0 rounded-full"
                          style={{
                            backgroundColor:
                              label?.color && label.color !== "" ? label.color : "#000",
                          }}
                        />
                        {label.name}
                        <XMarkIcon className="h-2 w-2 group-hover:text-red-500" />
                      </span>
                    );
                })}
                <Controller
                  control={control}
                  name="labels_list"
                  render={({ field: { value } }) => (
                    <Listbox
                      as="div"
                      value={value}
                      onChange={(val: any) => submitChanges({ labels_list: val })}
                      className="flex-shrink-0"
                      multiple
                      disabled={isNotAllowed}
                    >
                      {({ open }) => (
                        <div className="relative">
                          <Listbox.Button
                            className={`flex ${
                              isNotAllowed
                                ? "cursor-not-allowed"
                                : "cursor-pointer hover:bg-brand-surface-1"
                            } items-center gap-2 rounded-2xl border border-brand-base px-2 py-0.5 text-xs text-brand-secondary`}
                          >
                            Select Label
                          </Listbox.Button>

                          <Transition
                            show={open}
                            as={React.Fragment}
                            leave="transition ease-in duration-100"
                            leaveFrom="opacity-100"
                            leaveTo="opacity-0"
                          >
                            <Listbox.Options className="absolute right-0 z-10 mt-1 max-h-28 w-40 overflow-auto rounded-md bg-brand-surface-2 py-1 text-xs shadow-lg border border-brand-base focus:outline-none">
                              <div className="py-1">
                                {issueLabels ? (
                                  issueLabels.length > 0 ? (
                                    issueLabels.map((label: IIssueLabels) => {
                                      const children = issueLabels?.filter(
                                        (l) => l.parent === label.id
                                      );

                                      if (children.length === 0) {
                                        if (!label.parent)
                                          return (
                                            <Listbox.Option
                                              key={label.id}
                                              className={({ active, selected }) =>
                                                `${
                                                  active || selected ? "bg-brand-surface-1" : ""
                                                } ${
                                                  selected ? "" : "text-brand-secondary"
                                                } flex cursor-pointer select-none items-center gap-2 truncate p-2`
                                              }
                                              value={label.id}
                                            >
                                              <span
                                                className="h-2 w-2 flex-shrink-0 rounded-full"
                                                style={{
                                                  backgroundColor:
                                                    label.color && label.color !== ""
                                                      ? label.color
                                                      : "#000",
                                                }}
                                              />
                                              {label.name}
                                            </Listbox.Option>
                                          );
                                      } else
                                        return (
                                          <div className="border-y border-brand-base bg-brand-surface-1">
                                            <div className="flex select-none items-center gap-2 truncate p-2 font-medium text-brand-base">
                                              <RectangleGroupIcon className="h-3 w-3" />
                                              {label.name}
                                            </div>
                                            <div>
                                              {children.map((child) => (
                                                <Listbox.Option
                                                  key={child.id}
                                                  className={({ active, selected }) =>
                                                    `${active || selected ? "bg-brand-base" : ""} ${
                                                      selected ? "" : "text-brand-secondary"
                                                    } flex cursor-pointer select-none items-center gap-2 truncate p-2`
                                                  }
                                                  value={child.id}
                                                >
                                                  <span
                                                    className="h-2 w-2 flex-shrink-0 rounded-full"
                                                    style={{
                                                      backgroundColor: child?.color ?? "black",
                                                    }}
                                                  />
                                                  {child.name}
                                                </Listbox.Option>
                                              ))}
                                            </div>
                                          </div>
                                        );
                                    })
                                  ) : (
                                    <div className="text-center">No labels found</div>
                                  )
                                ) : (
                                  <Spinner />
                                )}
                              </div>
                            </Listbox.Options>
                          </Transition>
                        </div>
                      )}
                    </Listbox>
                  )}
                />
                {!isNotAllowed && (
                  <button
                    type="button"
                    className={`flex ${
                      isNotAllowed
                        ? "cursor-not-allowed"
                        : "cursor-pointer hover:bg-brand-surface-1"
                    } items-center gap-1 rounded-2xl border border-brand-base px-2 py-0.5 text-xs text-brand-secondary`}
                    onClick={() => setCreateLabelForm((prevData) => !prevData)}
                  >
                    {createLabelForm ? (
                      <>
                        <XMarkIcon className="h-3 w-3" /> Cancel
                      </>
                    ) : (
                      <>
                        <PlusIcon className="h-3 w-3" /> New
                      </>
                    )}
                  </button>
                )}
              </div>
            </div>
          </div>
          {createLabelForm && (
            <form className="flex items-center gap-x-2" onSubmit={handleSubmit(handleNewLabel)}>
              <div>
                <Popover className="relative">
                  {({ open }) => (
                    <>
                      <Popover.Button
                        className={`flex items-center gap-1 rounded-md bg-brand-surface-2 p-1 outline-none focus:ring-2 focus:ring-brand-accent`}
                      >
                        {watch("color") && watch("color") !== "" && (
                          <span
                            className="h-5 w-5 rounded"
                            style={{
                              backgroundColor: watch("color") ?? "black",
                            }}
                          />
                        )}
                        <ChevronDownIcon className="h-3 w-3" />
                      </Popover.Button>

                      <Transition
                        as={React.Fragment}
                        enter="transition ease-out duration-200"
                        enterFrom="opacity-0 translate-y-1"
                        enterTo="opacity-100 translate-y-0"
                        leave="transition ease-in duration-150"
                        leaveFrom="opacity-100 translate-y-0"
                        leaveTo="opacity-0 translate-y-1"
                      >
                        <Popover.Panel className="absolute right-0 bottom-8 z-10 mt-1 max-w-xs transform px-2 sm:px-0">
                          <Controller
                            name="color"
                            control={controlLabel}
                            render={({ field: { value, onChange } }) => (
                              <TwitterPicker
                                color={value}
                                onChange={(value) => onChange(value.hex)}
                              />
                            )}
                          />
                        </Popover.Panel>
                      </Transition>
                    </>
                  )}
                </Popover>
              </div>
              <Input
                id="name"
                name="name"
                placeholder="Title"
                register={register}
                validations={{
                  required: "This is required",
                }}
                autoComplete="off"
              />
              <button
                type="submit"
                className="grid place-items-center rounded bg-red-500 p-2.5"
                onClick={() => setCreateLabelForm(false)}
              >
                <XMarkIcon className="h-4 w-4 text-white" />
              </button>
              <button
                type="submit"
                className="grid place-items-center rounded bg-green-500 p-2.5"
                disabled={isSubmitting}
              >
                <PlusIcon className="h-4 w-4 text-white" />
              </button>
            </form>
          )}
        </div>
        <div className="min-h-[116px] py-1 text-xs">
          <div className="flex items-center justify-between gap-2">
            <h4>Links</h4>
            {!isNotAllowed && (
              <button
                type="button"
                className="grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-brand-surface-1"
                onClick={() => setLinkModal(true)}
              >
                <PlusIcon className="h-4 w-4" />
              </button>
            )}
          </div>
          <div className="mt-2 space-y-2">
            {issueDetail?.issue_link && issueDetail.issue_link.length > 0 ? (
              <LinksList
                links={issueDetail.issue_link}
                handleDeleteLink={handleDeleteLink}
                userAuth={memberRole}
              />
            ) : null}
          </div>
        </div>
      </div>
    </>
  );
};