import { getBoundingClientRect } from '@documenso/lib/client-only/get-bounding-client-rect';
import { useDocumentElement } from '@documenso/lib/client-only/hooks/use-document-element';
import { useCurrentEnvelopeEditor } from '@documenso/lib/client-only/providers/envelope-editor-provider';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import { FIELD_META_DEFAULT_VALUES } from '@documenso/lib/types/field-meta';
import { nanoid } from '@documenso/lib/universal/id';
import { canRecipientFieldsBeModified } from '@documenso/lib/utils/recipients';
import { SignatureIcon } from '@documenso/ui/icons/signature';
import { getRecipientColorStyles } from '@documenso/ui/lib/recipient-colors';
import { cn } from '@documenso/ui/lib/utils';
import { FRIENDLY_FIELD_TYPE } from '@documenso/ui/primitives/document-flow/types';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react/macro';
import { FieldType } from '@prisma/client';
import {
  CalendarIcon,
  CheckSquareIcon,
  ContactIcon,
  DiscIcon,
  HashIcon,
  ListIcon,
  MailIcon,
  TextIcon,
  UserIcon,
} from 'lucide-react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';

const MIN_HEIGHT_PX = 12;
const MIN_WIDTH_PX = 36;

const DEFAULT_HEIGHT_PX = MIN_HEIGHT_PX * 2.5;
const DEFAULT_WIDTH_PX = MIN_WIDTH_PX * 2.5;

export const fieldButtonList = [
  {
    type: FieldType.SIGNATURE,
    icon: SignatureIcon,
    name: msg`Signature`,
    className: 'font-signature text-lg',
  },
  {
    type: FieldType.EMAIL,
    icon: MailIcon,
    name: msg`Email`,
  },
  {
    type: FieldType.NAME,
    icon: UserIcon,
    name: msg`Name`,
  },
  {
    type: FieldType.INITIALS,
    icon: ContactIcon,
    name: msg`Initials`,
  },
  {
    type: FieldType.DATE,
    icon: CalendarIcon,
    name: msg`Date`,
  },
  {
    type: FieldType.TEXT,
    icon: TextIcon,
    name: msg`Text`,
  },
  {
    type: FieldType.NUMBER,
    icon: HashIcon,
    name: msg`Number`,
  },
  {
    type: FieldType.RADIO,
    icon: DiscIcon,
    name: msg`Radio`,
  },
  {
    type: FieldType.CHECKBOX,
    icon: CheckSquareIcon,
    name: msg`Checkbox`,
  },
  {
    type: FieldType.DROPDOWN,
    icon: ListIcon,
    name: msg`Dropdown`,
  },
];

type EnvelopeEditorFieldDragDropProps = {
  selectedRecipientId: number | null;
  selectedEnvelopeItemId: string | null;
};

export const EnvelopeEditorFieldDragDrop = ({
  selectedRecipientId,
  selectedEnvelopeItemId,
}: EnvelopeEditorFieldDragDropProps) => {
  const { envelope, editorFields, isTemplate, getRecipientColorKey } = useCurrentEnvelopeEditor();

  const { t } = useLingui();

  const [selectedField, setSelectedField] = useState<FieldType | null>(null);

  const { isWithinPageBounds, getPage } = useDocumentElement();

  const isFieldsDisabled = useMemo(() => {
    const selectedSigner = envelope.recipients.find((recipient) => recipient.id === selectedRecipientId);
    const fields = envelope.fields;

    if (!selectedSigner) {
      return true;
    }

    // Allow fields to be modified for templates regardless of anything.
    if (isTemplate) {
      return false;
    }

    return !canRecipientFieldsBeModified(selectedSigner, fields);
  }, [selectedRecipientId, envelope.recipients, envelope.fields]);

  const [isFieldWithinBounds, setIsFieldWithinBounds] = useState(false);
  const [coords, setCoords] = useState({
    x: 0,
    y: 0,
  });

  const fieldBounds = useRef({
    height: 0,
    width: 0,
  });

  const onMouseMove = useCallback(
    (event: MouseEvent) => {
      setIsFieldWithinBounds(
        isWithinPageBounds(event, PDF_VIEWER_PAGE_SELECTOR, fieldBounds.current.width, fieldBounds.current.height),
      );

      setCoords({
        x: event.clientX - fieldBounds.current.width / 2,
        y: event.clientY - fieldBounds.current.height / 2,
      });
    },
    [isWithinPageBounds],
  );

  const onMouseClick = useCallback(
    (event: MouseEvent) => {
      if (!selectedField || !selectedRecipientId || !selectedEnvelopeItemId) {
        return;
      }

      const $page = getPage(event, PDF_VIEWER_PAGE_SELECTOR);

      if (
        !$page ||
        !isWithinPageBounds(event, PDF_VIEWER_PAGE_SELECTOR, fieldBounds.current.width, fieldBounds.current.height)
      ) {
        setSelectedField(null);
        return;
      }

      const { top, left, height, width } = getBoundingClientRect($page);

      const pageNumber = parseInt($page.getAttribute('data-page-number') ?? '1', 10);

      // Calculate x and y as a percentage of the page width and height
      let pageX = ((event.pageX - left) / width) * 100;
      let pageY = ((event.pageY - top) / height) * 100;

      // Get the bounds as a percentage of the page width and height
      const fieldPageWidth = (fieldBounds.current.width / width) * 100;
      const fieldPageHeight = (fieldBounds.current.height / height) * 100;

      // And center it based on the bounds
      pageX -= fieldPageWidth / 2;
      pageY -= fieldPageHeight / 2;

      const field = {
        formId: nanoid(12),
        envelopeItemId: selectedEnvelopeItemId,
        type: selectedField,
        page: pageNumber,
        positionX: pageX,
        positionY: pageY,
        width: fieldPageWidth,
        height: fieldPageHeight,
        recipientId: selectedRecipientId,
        fieldMeta: structuredClone(FIELD_META_DEFAULT_VALUES[selectedField]),
      };

      editorFields.addField(field);

      setIsFieldWithinBounds(false);
      setSelectedField(null);
    },
    [isWithinPageBounds, selectedField, selectedRecipientId, selectedEnvelopeItemId, getPage, editorFields],
  );

  useEffect(() => {
    const observer = new MutationObserver((_mutations) => {
      const $page = document.querySelector(PDF_VIEWER_PAGE_SELECTOR);

      if (!$page) {
        return;
      }

      fieldBounds.current = {
        height: Math.max(DEFAULT_HEIGHT_PX),
        width: Math.max(DEFAULT_WIDTH_PX),
      };
    });

    observer.observe(document.body, {
      childList: true,
      subtree: true,
    });

    return () => {
      observer.disconnect();
    };
  }, []);

  useEffect(() => {
    if (selectedField) {
      window.addEventListener('mousemove', onMouseMove);
      window.addEventListener('mouseup', onMouseClick);
    }

    return () => {
      window.removeEventListener('mousemove', onMouseMove);
      window.removeEventListener('mouseup', onMouseClick);
    };
  }, [onMouseClick, onMouseMove, selectedField]);

  const selectedRecipientStyles = useMemo(
    () => getRecipientColorStyles(getRecipientColorKey(selectedRecipientId ?? -1)),
    [selectedRecipientId, getRecipientColorKey],
  );

  return (
    <>
      <div className="grid grid-cols-2 gap-x-2 gap-y-2.5">
        {fieldButtonList.map((field) => (
          <button
            disabled={isFieldsDisabled}
            key={field.type}
            type="button"
            onClick={() => setSelectedField(field.type)}
            onMouseDown={() => setSelectedField(field.type)}
            data-selected={selectedField === field.type ? true : undefined}
            className={cn(
              'group flex h-12 cursor-pointer items-center justify-center rounded-lg border border-border px-4 transition-colors',
              selectedRecipientStyles.fieldButton,
            )}
          >
            <p
              className={cn(
                'flex items-center justify-center gap-x-1.5 font-normal font-noto text-muted-foreground text-sm group-data-[selected]:text-foreground',
                field.className,
                selectedRecipientStyles.fieldButtonText,
              )}
            >
              {field.type !== FieldType.SIGNATURE && <field.icon className="h-4 w-4" />}
              {t(field.name)}
            </p>
          </button>
        ))}
      </div>

      {selectedField && (
        <div
          className={cn(
            'pointer-events-none fixed z-50 flex cursor-pointer flex-col items-center justify-center rounded-[2px] bg-white font-noto text-muted-foreground ring-2 transition duration-200 [container-type:size] dark:text-muted',
            selectedRecipientStyles.base,
            selectedField === FieldType.SIGNATURE && 'font-signature',
            {
              '-rotate-6 scale-90 opacity-50 dark:bg-black/20': !isFieldWithinBounds,
              'dark:text-black/60': isFieldWithinBounds,
            },
          )}
          style={{
            top: coords.y,
            left: coords.x,
            height: fieldBounds.current.height,
            width: fieldBounds.current.width,
          }}
        >
          <span className="text-[clamp(0.425rem,25cqw,0.825rem)]">{t(FRIENDLY_FIELD_TYPE[selectedField])}</span>
        </div>
      )}
    </>
  );
};
