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

import type { Ref } from "react";
import React, { useEffect, useState, useRef, Fragment } from "react";
import type { Placement } from "@popperjs/core";
import { Controller, useForm } from "react-hook-form"; // services
import { usePopper } from "react-popper";
import { AlertCircle } from "lucide-react";
import { Popover, Transition } from "@headlessui/react";
// pi dash imports
import type { EditorRefApi } from "@pi-dash/editor";
import { Button } from "@pi-dash/propel/button";
import { TOAST_TYPE, setToast } from "@pi-dash/propel/toast";
import { Input } from "@pi-dash/ui";
// components
import { RichTextEditor } from "@/components/editor/rich-text";
// services
import { AIService } from "@/services/ai.service";
const aiService = new AIService();

type Props = {
  isOpen: boolean;
  handleClose: () => void;
  onResponse: (response: any) => void;
  onError?: (error: any) => void;
  placement?: Placement;
  prompt?: string;
  button: React.ReactNode;
  className?: string;
  workspaceId: string;
  workspaceSlug: string;
  projectId: string;
};

type FormData = {
  prompt: string;
  task: string;
};

export function GptAssistantPopover(props: Props) {
  const {
    isOpen,
    handleClose,
    onResponse,
    onError,
    placement,
    prompt,
    button,
    className = "",
    workspaceId,
    workspaceSlug,
    projectId,
  } = props;
  // states
  const [response, setResponse] = useState("");
  const [invalidResponse, setInvalidResponse] = useState(false);
  const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
  const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
  // refs
  const editorRef = useRef<EditorRefApi>(null);
  const responseRef = useRef<EditorRefApi>(null);
  // popper
  const { styles, attributes } = usePopper(referenceElement, popperElement, {
    placement: placement ?? "auto",
  });
  // form
  const {
    handleSubmit,
    control,
    reset,
    setFocus,
    formState: { isSubmitting },
  } = useForm<FormData>({
    defaultValues: {
      prompt: prompt || "",
      task: "",
    },
  });

  const onClose = () => {
    handleClose();
    setResponse("");
    setInvalidResponse(false);
    reset();
  };

  const handleServiceError = (err: any) => {
    const error = err?.data?.error;
    const errorMessage =
      err?.status === 429
        ? error || "You have reached the maximum number of requests of 50 requests per month per user."
        : error || "Some error occurred. Please try again.";

    setToast({
      type: TOAST_TYPE.ERROR,
      title: "Error!",
      message: errorMessage,
    });

    if (onError) onError(err);
  };

  const callAIService = async (formData: FormData) => {
    try {
      const res = await aiService.createGptTask(workspaceSlug.toString(), {
        prompt: prompt || "",
        task: formData.task,
      });

      setResponse(res.response_html);
      setFocus("task");

      setInvalidResponse(res.response === "");
    } catch (err) {
      handleServiceError(err);
    }
  };

  const handleInvalidTask = () => {
    setToast({
      type: TOAST_TYPE.ERROR,
      title: "Error!",
      message: "Please enter some task to get AI assistance.",
    });
  };

  const handleAIResponse = async (formData: FormData) => {
    if (!workspaceSlug) return;

    if (formData.task === "") {
      handleInvalidTask();
      return;
    }

    await callAIService(formData);
  };

  useEffect(() => {
    if (isOpen) setFocus("task");
  }, [isOpen, setFocus]);

  useEffect(() => {
    editorRef.current?.setEditorValue(prompt || "");
  }, [editorRef, prompt]);

  useEffect(() => {
    responseRef.current?.setEditorValue(`<p>${response}</p>`);
  }, [response, responseRef]);

  useEffect(() => {
    const handleEnterKeyPress = (event: KeyboardEvent) => {
      if (event.key === "Enter" && !event.shiftKey) {
        event.preventDefault();
        handleSubmit(handleAIResponse)();
      }
    };

    const handleEscapeKeyPress = (event: KeyboardEvent) => {
      if (event.key === "Escape") {
        onClose();
      }
    };

    if (isOpen) {
      window.addEventListener("keydown", handleEnterKeyPress);
      window.addEventListener("keydown", handleEscapeKeyPress);
    }

    return () => {
      window.removeEventListener("keydown", handleEnterKeyPress);
      window.removeEventListener("keydown", handleEscapeKeyPress);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isOpen, handleSubmit, onClose]);

  const responseActionButton = response !== "" && (
    <Button
      variant="primary"
      onClick={() => {
        onResponse(response);
        onClose();
      }}
    >
      Use this response
    </Button>
  );

  const generateResponseButtonText = isSubmitting
    ? "Generating response..."
    : response === ""
      ? "Generate response"
      : "Generate again";

  return (
    <Popover as="div" className={`relative w-min text-left`}>
      <Popover.Button as={Fragment}>
        <button ref={setReferenceElement} className="flex items-center" tabIndex={-1}>
          {button}
        </button>
      </Popover.Button>
      <Transition
        show={isOpen}
        as={React.Fragment}
        enter="transition ease-out duration-100"
        enterFrom="transform opacity-0 scale-95"
        enterTo="transform opacity-100 scale-100"
        leave="transition ease-in duration-75"
        leaveFrom="transform opacity-100 scale-100"
        leaveTo="transform opacity-0 scale-95"
      >
        <Popover.Panel
          as="div"
          className={`shadow fixed z-10 flex w-full max-w-full min-w-[50rem] flex-col space-y-4 overflow-hidden rounded-[10px] border border-subtle bg-surface-1 p-4 ${className}`}
          ref={setPopperElement as Ref<HTMLDivElement>}
          style={styles.popper}
          {...attributes.popper}
        >
          <div className="vertical-scroll-enable max-h-72 space-y-4 overflow-y-auto">
            {prompt && (
              <div className="text-13">
                Content:
                <RichTextEditor
                  editable={false}
                  id="ai-assistant-content"
                  initialValue={prompt}
                  containerClassName="-m-3"
                  ref={editorRef}
                  workspaceId={workspaceId}
                  workspaceSlug={workspaceSlug}
                  projectId={projectId}
                />
              </div>
            )}
            {response !== "" && (
              <div className="page-block-section max-h-[8rem] text-13">
                Response:
                <RichTextEditor
                  editable={false}
                  id="ai-assistant-response"
                  initialValue={`<p>${response}</p>`}
                  ref={responseRef}
                  workspaceId={workspaceId}
                  workspaceSlug={workspaceSlug}
                  projectId={projectId}
                />
              </div>
            )}
            {invalidResponse && (
              <div className="text-13 text-danger-primary">
                No response could be generated. This may be due to insufficient content or task information. Please try
                again.
              </div>
            )}
          </div>
          <Controller
            control={control}
            name="task"
            render={({ field: { value, onChange, ref } }) => (
              <Input
                id="task"
                name="task"
                type="text"
                value={value}
                onChange={onChange}
                ref={ref}
                placeholder={`${
                  prompt && prompt !== "" ? "Tell AI what action to perform on this content..." : "Ask AI anything..."
                }`}
                className="w-full"
                autoFocus
              />
            )}
          />
          <div className="flex justify-between gap-2">
            {responseActionButton ? (
              <>{responseActionButton}</>
            ) : (
              <>
                <div className="flex items-start justify-center gap-2 text-13 text-accent-primary">
                  <AlertCircle className="h-4 w-4" />
                  <p>By using this feature, you consent to sharing the message with a 3rd party service. </p>
                </div>
              </>
            )}
            <div className="flex items-center gap-2">
              <Button variant="secondary" onClick={onClose}>
                Close
              </Button>
              <Button variant="primary" onClick={handleSubmit(handleAIResponse)} loading={isSubmitting}>
                {generateResponseButtonText}
              </Button>
            </div>
          </div>
        </Popover.Panel>
      </Transition>
    </Popover>
  );
}
