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

import type { Node } from "@tiptap/pm/model";
import { Link2Off } from "lucide-react";
import { useCallback, useEffect, useRef, useState } from "react";
// components
import type { LinkViewProps, LinkViews } from "@/components/links";
// helpers
import { isValidHttpUrl } from "@/helpers/common";

type InputViewProps = {
  label: string;
  value: string;
  placeholder: string;
  onChange: (value: string) => void;
  autoFocus?: boolean;
};

function InputView({ label, value, placeholder, onChange, autoFocus }: InputViewProps) {
  return (
    <div className="flex flex-col gap-1">
      <label className="inline-block text-11 font-semibold text-placeholder">{label}</label>
      <input
        placeholder={placeholder}
        onClick={(e) => e.stopPropagation()}
        className="w-[280px] rounded-md border border-strong bg-layer-1 p-2 text-13 text-primary outline-none"
        value={value}
        onChange={(e) => onChange(e.target.value)}
        autoFocus={autoFocus}
      />
    </div>
  );
}

type LinkEditViewProps = {
  viewProps: LinkViewProps;
  switchView: (view: LinkViews) => void;
};

export function LinkEditView({ viewProps }: LinkEditViewProps) {
  const { editor, from, to, url: initialUrl, text: initialText, closeLinkView } = viewProps;

  // State
  const [positionRef] = useState({ from, to });
  const [localUrl, setLocalUrl] = useState(initialUrl);
  const [localText, setLocalText] = useState(initialText ?? "");
  const [linkRemoved, setLinkRemoved] = useState(false);
  const hasSubmitted = useRef(false);

  const removeLink = useCallback(() => {
    editor.view.dispatch(editor.state.tr.removeMark(from, to, editor.schema.marks.link));
    setLinkRemoved(true);
    closeLinkView();
  }, [editor, from, to, closeLinkView]);

  // Effects
  useEffect(
    () =>
      // Cleanup effect: Remove link if not submitted and url is empty
      () => {
        if (!hasSubmitted.current && !linkRemoved && initialUrl === "") {
          try {
            removeLink();
          } catch (e) {
            console.error("Error removing link", e);
          }
        }
      },
    [removeLink, linkRemoved, initialUrl]
  );

  // Sync state with props
  useEffect(() => {
    setLocalUrl(initialUrl);
  }, [initialUrl]);

  useEffect(() => {
    if (initialText) setLocalText(initialText);
  }, [initialText]);

  // Handlers
  const handleTextChange = useCallback((value: string) => {
    if (value.trim() !== "") setLocalText(value);
  }, []);

  const applyChanges = useCallback((): boolean => {
    if (linkRemoved) return false;
    hasSubmitted.current = true;

    const { url, isValid } = isValidHttpUrl(localUrl);
    if (to >= editor.state.doc.content.size || !isValid) return false;

    // Apply URL change
    const tr = editor.state.tr;
    tr.removeMark(from, to, editor.schema.marks.link).addMark(from, to, editor.schema.marks.link.create({ href: url }));
    editor.view.dispatch(tr);

    // Apply text change if different
    if (localText !== initialText) {
      const node = editor.view.state.doc.nodeAt(from) as Node;
      if (!node || !node.marks) return false;

      editor
        .chain()
        .setTextSelection(from)
        .deleteRange({ from: positionRef.from, to: positionRef.to })
        .insertContent(localText)
        .setTextSelection({ from, to: from + localText.length })
        .run();
      //
      // Restore marks
      node.marks.forEach((mark) => {
        editor.chain().setMark(mark.type.name, mark.attrs).run();
      });
    }

    return true;
  }, [linkRemoved, positionRef, editor, from, to, initialText, localText, localUrl]);

  const handleKeyDown = useCallback(
    (e: React.KeyboardEvent) => {
      if (e.key === "Enter") {
        e.stopPropagation();
        if (applyChanges()) {
          closeLinkView();
          setLocalUrl("");
          setLocalText("");
        }
      }
    },
    [applyChanges, closeLinkView]
  );

  return (
    <div
      onKeyDown={handleKeyDown}
      className="shadow-md animate-in fade-in flex translate-y-1 flex-col gap-3 rounded-sm border-2 border-subtle bg-layer-1 p-2"
      style={{
        transition: "all 0.1s cubic-bezier(.55, .085, .68, .53)",
      }}
      tabIndex={0}
    >
      <InputView label="URL" placeholder="Enter or paste URL" value={localUrl} onChange={setLocalUrl} autoFocus />
      <InputView label="Text" placeholder="Enter Text to display" value={localText} onChange={handleTextChange} />
      <div className="bg-strong mb-1 h-[1px] w-full gap-2" />
      <div className="flex items-center gap-2 text-13 text-secondary">
        <Link2Off size={14} className="inline-block" />
        <button onClick={removeLink} className="cursor-pointer transition-colors hover:text-placeholder">
          Remove Link
        </button>
      </div>
    </div>
  );
}
