import { useElementBounds } from '@documenso/lib/client-only/hooks/use-element-bounds';
import { useIsPageInDom } from '@documenso/lib/client-only/hooks/use-is-page-in-dom';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import type { TFieldMetaSchema } from '@documenso/lib/types/field-meta';
import { ZCheckboxFieldMeta, ZRadioFieldMeta } from '@documenso/lib/types/field-meta';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { FieldType } from '@prisma/client';
import { CopyPlus, Settings2, SquareStack, Trash } from 'lucide-react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { Rnd } from 'react-rnd';
import { useSearchParams } from 'react-router';

import { getRecipientColorStyles } from '../../lib/recipient-colors';
import { cn } from '../../lib/utils';
import { FieldContent } from './field-content';
import type { TDocumentFlowFormSchema } from './types';

type Field = TDocumentFlowFormSchema['fields'][0];

export type FieldItemProps = {
  field: Field;
  fieldClassName?: string;
  passive?: boolean;
  disabled?: boolean;
  minHeight?: number;
  minWidth?: number;
  defaultHeight?: number;
  defaultWidth?: number;
  onResize?: (_node: HTMLElement) => void;
  onMove?: (_node: HTMLElement) => void;
  onRemove?: () => void;
  onDuplicate?: () => void;
  onDuplicateAllPages?: () => void;
  onAdvancedSettings?: () => void;
  onFocus?: () => void;
  onBlur?: () => void;
  onMouseEnter?: () => void;
  onMouseLeave?: () => void;
  recipientIndex?: number;
  hasErrors?: boolean;
  active?: boolean;
  onFieldActivate?: () => void;
  onFieldDeactivate?: () => void;
};

/**
 * The item when editing fields??
 */
export const FieldItem = (props: FieldItemProps) => {
  const isPageInDom = useIsPageInDom(props.field.pageNumber);

  if (!isPageInDom) {
    return null;
  }

  return <FieldItemInner {...props} />;
};

const FieldItemInner = ({
  fieldClassName,
  field,
  passive,
  disabled,
  minHeight,
  minWidth,
  defaultHeight,
  defaultWidth,
  onResize,
  onMove,
  onRemove,
  onDuplicate,
  onDuplicateAllPages,
  onAdvancedSettings,
  onFocus,
  onBlur,
  recipientIndex = 0,
  hasErrors,
  active,
  onFieldActivate,
  onFieldDeactivate,
}: FieldItemProps) => {
  const { _ } = useLingui();
  const [searchParams] = useSearchParams();

  const [coords, setCoords] = useState({
    pageX: 0,
    pageY: 0,
    pageHeight: defaultHeight || 0,
    pageWidth: defaultWidth || 0,
  });
  const [settingsActive, setSettingsActive] = useState(false);
  const $el = useRef<HTMLDivElement>(null);

  const $pageBounds = useElementBounds(`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.pageNumber}"]`);

  const signerStyles = getRecipientColorStyles(recipientIndex);

  const isDevMode = searchParams.get('devmode') === 'true';

  const advancedField = [
    'NUMBER',
    'RADIO',
    'CHECKBOX',
    'DROPDOWN',
    'TEXT',
    'INITIALS',
    'EMAIL',
    'DATE',
    'NAME',
  ].includes(field.type);

  const calculateCoords = useCallback(() => {
    const $page = document.querySelector<HTMLElement>(
      `${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.pageNumber}"]`,
    );

    if (!$page) {
      return;
    }

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

    const top = $page.getBoundingClientRect().top + window.scrollY;
    const left = $page.getBoundingClientRect().left + window.scrollX;

    // X and Y are percentages of the page's height and width
    const pageX = (field.pageX / 100) * width + left;
    const pageY = (field.pageY / 100) * height + top;

    const pageHeight = (field.pageHeight / 100) * height;
    const pageWidth = (field.pageWidth / 100) * width;

    setCoords({
      pageX: pageX,
      pageY: pageY,
      pageHeight: pageHeight,
      pageWidth: pageWidth,
    });
  }, [field.pageHeight, field.pageNumber, field.pageWidth, field.pageX, field.pageY]);

  useEffect(() => {
    calculateCoords();
  }, [calculateCoords]);

  useEffect(() => {
    const onResize = () => {
      calculateCoords();
    };

    window.addEventListener('resize', onResize);

    return () => {
      window.removeEventListener('resize', onResize);
    };
  }, [calculateCoords]);

  useEffect(() => {
    const onClickOutsideOfField = (event: MouseEvent) => {
      const isOutsideOfField = $el.current && !event.composedPath().includes($el.current);

      setSettingsActive((active) => {
        if (active && isOutsideOfField) {
          return false;
        }

        return active;
      });

      if (isOutsideOfField) {
        setSettingsActive(false);
        onFieldDeactivate?.();
        onBlur?.();
      }
    };

    document.body.addEventListener('click', onClickOutsideOfField);

    return () => {
      document.body.removeEventListener('click', onClickOutsideOfField);
    };
  }, [onBlur]);

  const hasFieldMetaValues = (
    fieldType: string,
    fieldMeta: TFieldMetaSchema,
    parser: typeof ZCheckboxFieldMeta | typeof ZRadioFieldMeta,
  ) => {
    if (field.type !== fieldType || !fieldMeta) {
      return false;
    }

    const parsedMeta = parser?.parse(fieldMeta);
    return parsedMeta && parsedMeta.values && parsedMeta.values.length > 0;
  };

  const checkBoxHasValues = useMemo(
    () => hasFieldMetaValues('CHECKBOX', field.fieldMeta, ZCheckboxFieldMeta),
    [field.fieldMeta],
  );

  const radioHasValues = useMemo(
    () => hasFieldMetaValues('RADIO', field.fieldMeta, ZRadioFieldMeta),
    [field.fieldMeta],
  );

  const hasCheckedValues = (fieldMeta: TFieldMetaSchema, type: FieldType) => {
    if (!fieldMeta || (type !== FieldType.RADIO && type !== FieldType.CHECKBOX)) {
      return false;
    }

    if (type === FieldType.RADIO) {
      const parsed = ZRadioFieldMeta.parse(fieldMeta);
      return parsed.values?.some((value) => value.checked) ?? false;
    }

    if (type === FieldType.CHECKBOX) {
      const parsed = ZCheckboxFieldMeta.parse(fieldMeta);
      return parsed.values?.some((value) => value.checked) ?? false;
    }

    return false;
  };

  const fieldHasCheckedValues = useMemo(
    () => hasCheckedValues(field.fieldMeta, field.type),
    [field.fieldMeta, field.type],
  );

  const fixedSize = checkBoxHasValues || radioHasValues;

  return createPortal(
    <Rnd
      key={coords.pageX + coords.pageY + coords.pageHeight + coords.pageWidth}
      className={cn('dark-mode-disabled group', {
        'pointer-events-none': passive,
        'pointer-events-none cursor-not-allowed opacity-75': disabled,
        'z-50': active && !disabled,
        'z-20': !active && !disabled,
        'z-10': disabled,
      })}
      minHeight={fixedSize ? '' : minHeight || 'auto'}
      minWidth={fixedSize ? '' : minWidth || 'auto'}
      default={{
        x: coords.pageX,
        y: coords.pageY,
        height: fixedSize ? 'auto' : coords.pageHeight,
        width: fixedSize ? 'auto' : coords.pageWidth,
      }}
      maxWidth={fixedSize && $pageBounds?.width ? $pageBounds.width - coords.pageX : undefined}
      bounds={`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.pageNumber}"]`}
      onDragStart={() => onFieldActivate?.()}
      onResizeStart={() => onFieldActivate?.()}
      onMouseEnter={() => onFocus?.()}
      onMouseLeave={() => onBlur?.()}
      enableResizing={!fixedSize}
      resizeHandleStyles={{
        bottom: { bottom: -8, cursor: 'ns-resize' },
        top: { top: -8, cursor: 'ns-resize' },
        left: { cursor: 'ew-resize' },
        right: { cursor: 'ew-resize' },
      }}
      onResizeStop={(_e, _d, ref) => {
        onFieldDeactivate?.();
        onResize?.(ref);
      }}
      onDragStop={(_e, d) => {
        onFieldDeactivate?.();
        onMove?.(d.node);
      }}
    >
      {(field.type === FieldType.RADIO || field.type === FieldType.CHECKBOX) && field.fieldMeta?.label && (
        <div
          className={cn('absolute -top-16 right-0 left-0 rounded-md p-2 text-center text-gray-700 text-xs', {
            'border border-primary bg-foreground/5': !fieldHasCheckedValues,
            'border border-primary bg-documenso-200': fieldHasCheckedValues,
          })}
        >
          {field.fieldMeta.label}
        </div>
      )}

      <div
        className={cn(
          'group/field-item relative flex h-full w-full items-center justify-center rounded-[2px] bg-white/90 px-2 ring-2 transition-colors',
          !hasErrors && signerStyles.base,
          !hasErrors && signerStyles.fieldItem,
          fieldClassName,
          {
            'rounded-[2px] border bg-red-400/20 shadow-[0_0_0_5px_theme(colors.red.500/10%),0_0_0_2px_theme(colors.red.500/40%),0_0_0_0.5px_theme(colors.red.500)] ring-red-400':
              hasErrors,
          },
          !fixedSize && '[container-type:size]',
        )}
        data-error={hasErrors ? 'true' : undefined}
        onClick={(e) => {
          e.stopPropagation();
          setSettingsActive((prev) => !prev);
          onFieldActivate?.();
          onFocus?.();
        }}
        ref={$el}
        data-field-id={field.nativeId}
        data-field-type={field.type}
        data-recipient-id={field.recipientId}
      >
        <FieldContent field={field} />

        {/* On hover, display recipient initials on side of field.  */}
        <div className="absolute top-0 -right-5 z-20 hidden h-full w-5 items-center justify-center group-hover:flex">
          <div
            className={cn(
              'flex h-5 w-5 flex-col items-center justify-center rounded-r-md font-bold text-[0.5rem] text-white opacity-0 transition duration-200 group-hover/field-item:opacity-100',
              signerStyles.fieldItemInitials,
              {
                '!opacity-50': disabled || passive,
              },
            )}
          >
            {(field.signerEmail?.charAt(0)?.toUpperCase() ?? '') + (field.signerEmail?.charAt(1)?.toUpperCase() ?? '')}
          </div>
        </div>

        {isDevMode && (
          <div className="absolute bottom-full left-1/2 z-50 mb-1 -translate-x-1/2 rounded-md border border-border bg-background/95 px-2 py-1 shadow-sm backdrop-blur-sm">
            <div className="flex flex-col gap-0.5 text-[9px]">
              {field.nativeId && (
                <span>
                  <span className="text-muted-foreground">
                    <Trans>Field ID:</Trans>
                  </span>{' '}
                  <span className="font-mono text-foreground">{field.nativeId}</span>
                </span>
              )}
              <span>
                <span className="text-muted-foreground">
                  <Trans>Recipient ID:</Trans>
                </span>{' '}
                <span className="font-mono text-foreground">{field.recipientId}</span>
              </span>
              <span>
                <span className="text-muted-foreground">
                  <Trans>Pos X:</Trans>
                </span>{' '}
                <span className="font-mono text-foreground">{field.pageX.toFixed(2)}</span>
              </span>
              <span>
                <span className="text-muted-foreground">
                  <Trans>Pos Y:</Trans>
                </span>{' '}
                <span className="font-mono text-foreground">{field.pageY.toFixed(2)}</span>
              </span>
              <span>
                <span className="text-muted-foreground">
                  <Trans>Width:</Trans>
                </span>{' '}
                <span className="font-mono text-foreground">{field.pageWidth.toFixed(2)}</span>
              </span>
              <span>
                <span className="text-muted-foreground">
                  <Trans>Height:</Trans>
                </span>{' '}
                <span className="font-mono text-foreground">{field.pageHeight.toFixed(2)}</span>
              </span>
            </div>
          </div>
        )}
      </div>

      {!disabled && settingsActive && (
        <div className="absolute z-[60] mt-1 flex w-full items-center justify-center">
          <div className="group flex items-center justify-evenly gap-x-1 rounded-md border bg-gray-900 p-0.5">
            {advancedField && (
              <button
                title={_(msg`Advanced settings`)}
                className="rounded-sm p-1.5 text-gray-400 transition-colors hover:bg-white/10 hover:text-gray-100"
                onClick={onAdvancedSettings}
                onTouchEnd={onAdvancedSettings}
              >
                <Settings2 className="h-3 w-3" />
              </button>
            )}

            <button
              title={_(msg`Duplicate`)}
              className="rounded-sm p-1.5 text-gray-400 transition-colors hover:bg-white/10 hover:text-gray-100"
              onClick={onDuplicate}
              onTouchEnd={onDuplicate}
            >
              <CopyPlus className="h-3 w-3" />
            </button>

            <button
              title={_(msg`Duplicate on all pages`)}
              className="rounded-sm p-1.5 text-gray-400 transition-colors hover:bg-white/10 hover:text-gray-100"
              onClick={onDuplicateAllPages}
              onTouchEnd={onDuplicateAllPages}
            >
              <SquareStack className="h-3 w-3" />
            </button>

            <button
              title={_(msg`Remove`)}
              className="rounded-sm p-1.5 text-gray-400 transition-colors hover:bg-white/10 hover:text-gray-100"
              onClick={onRemove}
              onTouchEnd={onRemove}
            >
              <Trash className="h-3 w-3" />
            </button>
          </div>
        </div>
      )}
    </Rnd>,
    document.body,
  );
};
