/**
 * Copyright (c) 2023-present Pi Dash Software, Inc. and contributors
 * SPDX-License-Identifier: AGPL-3.0-only
 * See the LICENSE file for details.
 */

import React, { useEffect, useState } from "react";
import { observer } from "mobx-react";
import type { Control } from "react-hook-form";
import { Controller } from "react-hook-form";
import { Sparkle } from "lucide-react";
// pi dash imports
import { ETabIndices } from "@pi-dash/constants";
import type { EditorRefApi } from "@pi-dash/editor";
import { useTranslation } from "@pi-dash/i18n";
import { TOAST_TYPE, setToast } from "@pi-dash/propel/toast";
import type { TIssue } from "@pi-dash/types";
import { EFileAssetType } from "@pi-dash/types";
import { Loader } from "@pi-dash/ui";
import { getDescriptionPlaceholderI18n, getTabIndex } from "@pi-dash/utils";
// components
import { GptAssistantPopover } from "@/components/core/modals/gpt-assistant-popover";
import { RichTextEditor } from "@/components/editor/rich-text";
// helpers
// hooks
import { useEditorAsset } from "@/hooks/store/use-editor-asset";
import { useInstance } from "@/hooks/store/use-instance";
import { useWorkspace } from "@/hooks/store/use-workspace";
import useKeypress from "@/hooks/use-keypress";
import { usePlatformOS } from "@/hooks/use-platform-os";
// pi dash web services
import { WorkspaceService } from "@/services/workspace.service";
// services
import { AIService } from "@/services/ai.service";
const workspaceService = new WorkspaceService();
const aiService = new AIService();

type TIssueDescriptionEditorProps = {
  control: Control<TIssue>;
  isDraft: boolean;
  issueName: string;
  issueId: string | undefined;
  descriptionHtmlData: string | undefined;
  editorRef: React.MutableRefObject<EditorRefApi | null>;
  submitBtnRef: React.MutableRefObject<HTMLButtonElement | null>;
  gptAssistantModal: boolean;
  workspaceSlug: string;
  projectId: string | null;
  handleFormChange: () => void;
  handleDescriptionHTMLDataChange: (descriptionHtmlData: string) => void;
  setGptAssistantModal: React.Dispatch<React.SetStateAction<boolean>>;
  handleGptAssistantClose: () => void;
  onAssetUpload: (assetId: string) => void;
  onClose: () => void;
};

export const IssueDescriptionEditor = observer(function IssueDescriptionEditor(props: TIssueDescriptionEditorProps) {
  const {
    control,
    isDraft,
    issueName,
    issueId,
    descriptionHtmlData,
    editorRef,
    submitBtnRef,
    gptAssistantModal,
    workspaceSlug,
    projectId,
    handleFormChange,
    handleDescriptionHTMLDataChange,
    setGptAssistantModal,
    handleGptAssistantClose,
    onAssetUpload,
    onClose,
  } = props;
  // i18n
  const { t } = useTranslation();
  // states
  const [iAmFeelingLucky, setIAmFeelingLucky] = useState(false);
  // store hooks
  const { getWorkspaceBySlug } = useWorkspace();
  const workspaceId = getWorkspaceBySlug(workspaceSlug?.toString())?.id ?? "";
  const { config } = useInstance();
  const { uploadEditorAsset, duplicateEditorAsset } = useEditorAsset();
  // platform
  const { isMobile } = usePlatformOS();

  const { getIndex } = getTabIndex(ETabIndices.ISSUE_FORM, isMobile);

  useEffect(() => {
    if (descriptionHtmlData) handleDescriptionHTMLDataChange(descriptionHtmlData);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [descriptionHtmlData]);

  const handleKeyDown = (event: KeyboardEvent) => {
    if (editorRef.current?.isEditorReadyToDiscard()) {
      onClose();
    } else {
      setToast({
        type: TOAST_TYPE.ERROR,
        title: "Error!",
        message: "Editor is still processing changes. Please wait before proceeding.",
      });
      event.preventDefault(); // Prevent default action if editor is not ready to discard
    }
  };

  useKeypress("Escape", handleKeyDown);

  // handlers
  const handleAiAssistance = async (response: string) => {
    if (!workspaceSlug || !projectId) return;

    editorRef.current?.setEditorValueAtCursorPosition(response);
  };

  const handleAutoGenerateDescription = async () => {
    if (!workspaceSlug || !projectId) return;

    setIAmFeelingLucky(true);

    aiService
      .createGptTask(workspaceSlug.toString(), {
        prompt: issueName,
        task: "Generate a proper description for this work item.",
      })
      .then((res) => {
        if (res.response === "")
          setToast({
            type: TOAST_TYPE.ERROR,
            title: "Error!",
            message:
              "Work item title isn't informative enough to generate the description. Please try with a different title.",
          });
        else handleAiAssistance(res.response_html);
      })
      .catch((err) => {
        const error = err?.data?.error;

        if (err.status === 429)
          setToast({
            type: TOAST_TYPE.ERROR,
            title: "Error!",
            message: error || "You have reached the maximum number of requests of 50 requests per month per user.",
          });
        else
          setToast({
            type: TOAST_TYPE.ERROR,
            title: "Error!",
            message: error || "Some error occurred. Please try again.",
          });
      })
      .finally(() => setIAmFeelingLucky(false));
  };

  return (
    <div className="relative rounded-lg border-[0.5px] border-subtle-1 bg-layer-2">
      {descriptionHtmlData === undefined || !projectId ? (
        <Loader className="max-h-64 min-h-[120px] space-y-2 overflow-hidden rounded-md border border-subtle p-3 py-2 pt-3">
          <Loader.Item width="100%" height="26px" />
          <div className="flex items-center gap-2">
            <Loader.Item width="26px" height="26px" />
            <Loader.Item width="400px" height="26px" />
          </div>
          <div className="flex items-center gap-2">
            <Loader.Item width="26px" height="26px" />
            <Loader.Item width="400px" height="26px" />
          </div>
          <Loader.Item width="80%" height="26px" />
          <div className="flex items-center gap-2">
            <Loader.Item width="50%" height="26px" />
          </div>
          <div className="border-0.5 absolute right-3.5 bottom-2 z-10 flex items-center gap-2">
            <Loader.Item width="100px" height="26px" />
            <Loader.Item width="50px" height="26px" />
          </div>
        </Loader>
      ) : (
        <>
          <Controller
            name="description_html"
            control={control}
            render={({ field: { value, onChange } }) => (
              <RichTextEditor
                editable
                id="issue-modal-editor"
                initialValue={value ?? ""}
                value={descriptionHtmlData}
                workspaceSlug={workspaceSlug?.toString()}
                workspaceId={workspaceId}
                projectId={projectId}
                onChange={(_description: object, description_html: string) => {
                  onChange(description_html);
                  handleFormChange();
                }}
                onEnterKeyPress={() => submitBtnRef?.current?.click()}
                ref={editorRef}
                tabIndex={getIndex("description_html")}
                placeholder={(isFocused, description) => t(getDescriptionPlaceholderI18n(isFocused, description))}
                searchMentionCallback={async (payload) =>
                  await workspaceService.searchEntity(workspaceSlug?.toString() ?? "", {
                    ...payload,
                    project_id: projectId?.toString() ?? "",
                  })
                }
                containerClassName="pt-3 min-h-[120px]"
                uploadFile={async (blockId, file) => {
                  try {
                    const { asset_id } = await uploadEditorAsset({
                      blockId,
                      data: {
                        entity_identifier: issueId ?? "",
                        entity_type: isDraft
                          ? EFileAssetType.DRAFT_ISSUE_DESCRIPTION
                          : EFileAssetType.ISSUE_DESCRIPTION,
                      },
                      file,
                      projectId,
                      workspaceSlug,
                    });
                    onAssetUpload(asset_id);
                    return asset_id;
                  } catch (error) {
                    console.log("Error in uploading issue asset:", error);
                    throw new Error("Asset upload failed. Please try again later.");
                  }
                }}
                duplicateFile={async (assetId: string) => {
                  try {
                    const { asset_id } = await duplicateEditorAsset({
                      assetId,
                      entityId: issueId,
                      entityType: isDraft ? EFileAssetType.DRAFT_ISSUE_DESCRIPTION : EFileAssetType.ISSUE_DESCRIPTION,
                      projectId,
                      workspaceSlug,
                    });
                    onAssetUpload(asset_id);
                    return asset_id;
                  } catch {
                    throw new Error("Asset duplication failed. Please try again later.");
                  }
                }}
              />
            )}
          />
          <div className="border-0.5 z-10 flex items-center justify-end gap-2 p-3">
            {issueName && issueName.trim() !== "" && config?.has_llm_configured && (
              <button
                type="button"
                className={`flex items-center gap-1 rounded-sm bg-surface-2 px-1.5 py-1 text-caption-sm-regular hover:bg-layer-1 ${
                  iAmFeelingLucky ? "cursor-wait" : ""
                }`}
                onClick={handleAutoGenerateDescription}
                disabled={iAmFeelingLucky}
                tabIndex={getIndex("feeling_lucky")}
              >
                {iAmFeelingLucky ? (
                  "Generating response"
                ) : (
                  <>
                    <Sparkle className="h-3.5 w-3.5" />I{"'"}m feeling lucky
                  </>
                )}
              </button>
            )}
            {config?.has_llm_configured && projectId && (
              <GptAssistantPopover
                isOpen={gptAssistantModal}
                handleClose={() => {
                  setGptAssistantModal((prevData) => !prevData);
                  // this is done so that the title do not reset after gpt popover closed
                  handleGptAssistantClose();
                }}
                onResponse={(response) => {
                  handleAiAssistance(response);
                }}
                placement="top-end"
                button={
                  <button
                    type="button"
                    className="flex items-center gap-1 rounded-sm bg-surface-2 px-1.5 py-1 text-caption-sm-regular hover:bg-layer-1"
                    onClick={() => setGptAssistantModal((prevData) => !prevData)}
                    tabIndex={-1}
                  >
                    <Sparkle className="h-4 w-4" />
                    AI
                  </button>
                }
                workspaceId={workspaceId}
                workspaceSlug={workspaceSlug}
                projectId={projectId}
              />
            )}
          </div>
        </>
      )}
    </div>
  );
});
