import React, { useMemo } from 'react';
import DesktopPreviewCard from './DesktopPreviewCard';
import { resolveCorrespondents } from '../documents/correspondents';
import { getTagColorStyle } from '../utils/colors';
import { preventAll } from './events';

type DocumentLike = {
  id?: string | number;
  title?: string;
  tags?: Array<{ id?: string | number; label?: string; color?: string | null }>;
  [key: string]: unknown;
};

interface PendingRemovalTag {
  docId?: string | number;
  tagId?: string | number;
}

interface DesktopDocumentCardProps {
  doc: DocumentLike;
  style?: React.CSSProperties;
  shouldLoad?: boolean;
  dragging?: boolean;
  matchesFilter?: boolean;
  tagTargetActive?: boolean;
  tagTargetPending?: boolean;
  selected?: boolean;
  docTagTokens?: string;
  ensureAssetUrl?: (...args: any[]) => Promise<unknown>;
  getDocumentAsset?: (...args: any[]) => unknown;
  handleNavigatorSnapshot?: (...args: any[]) => void;
  cardPointerHandlers?: React.HTMLAttributes<HTMLDivElement>;
  onInspectDocument?: (id: string | number) => void;
  onTagDragEnter?: (event: React.DragEvent<HTMLDivElement>, docId: string | number) => void;
  onTagDragOver?: (event: React.DragEvent<HTMLDivElement>, docId: string | number) => void;
  onTagDragLeave?: (event: React.DragEvent<HTMLDivElement>, docId: string | number) => void;
  onTagDrop?: (event: React.DragEvent<HTMLDivElement>, doc: DocumentLike) => void;
  onDocTagPointerDown?: (event: React.PointerEvent<HTMLElement>, doc: DocumentLike, tag: any) => void;
  onDocTagDragStart?: (event: React.DragEvent<HTMLElement>, doc: DocumentLike, tag: any) => void;
  onDocTagDrag?: (event: React.DragEvent<HTMLElement>) => void;
  onDocTagDragEnd?: (event: React.DragEvent<HTMLElement>) => void;
  pendingRemovalTag?: PendingRemovalTag | null;
  registerNode?: (node: HTMLDivElement | null) => void;
}

const DesktopDocumentCard: React.FC<DesktopDocumentCardProps> = ({
  doc,
  style,
  shouldLoad,
  dragging,
  matchesFilter,
  tagTargetActive,
  tagTargetPending,
  selected,
  docTagTokens,
  ensureAssetUrl,
  getDocumentAsset,
  handleNavigatorSnapshot,
  cardPointerHandlers,
  onInspectDocument,
  onTagDragEnter,
  onTagDragOver,
  onTagDragLeave,
  onTagDrop,
  onDocTagPointerDown,
  onDocTagDragStart,
  onDocTagDrag,
  onDocTagDragEnd,
  pendingRemovalTag,
  registerNode,
}) => {
  const correspondents = useMemo(() => resolveCorrespondents(doc), [doc]);
  const tags = Array.isArray(doc?.tags) ? doc.tags : [];

  const itemClasses = ['desk-item'];
  if (dragging) itemClasses.push('is-dragging');
  if (tagTargetActive) itemClasses.push('is-tag-target');
  if (tagTargetPending) itemClasses.push('is-tag-pending');
  if (!matchesFilter) itemClasses.push('is-filtered-out');
  if (selected) itemClasses.push('is-selected');

  const ariaHidden = matchesFilter ? undefined : 'true';
  const dataTagIds = docTagTokens || undefined;

  return (
    <div
      key={doc.id}
      className={itemClasses.join(' ')}
      style={style}
      role="button"
      data-doc-id={doc.id}
      data-tag-ids={dataTagIds}
      aria-hidden={ariaHidden}
      ref={registerNode ?? undefined}
      {...cardPointerHandlers}
      onDragEnter={(event) => {
        if (doc?.id == null) {
          return;
        }
        onTagDragEnter?.(event, doc.id);
      }}
      onDragOver={(event) => {
        if (doc?.id == null) {
          return;
        }
        onTagDragOver?.(event, doc.id);
      }}
      onDragLeave={(event) => {
        if (doc?.id == null) {
          return;
        }
        onTagDragLeave?.(event, doc.id);
      }}
      onDrop={(event) => {
        onTagDrop?.(event, doc);
      }}
      onKeyDown={(event) => {
        if (event.key === 'Enter' || event.key === ' ') {
          preventAll(event);
          onInspectDocument?.(doc.id);
        }
      }}
    >
      <div className="desk-item__body">
        <DesktopPreviewCard
          doc={doc}
          title={doc.title}
          ensureAssetUrl={ensureAssetUrl}
          getDocumentAsset={getDocumentAsset}
          onNavigatorSnapshot={handleNavigatorSnapshot}
          shouldLoad={shouldLoad}
        />
        {correspondents.length > 0 && (
          <div className="desk-item__correspondents" aria-hidden="true">
            {correspondents.map((correspondent) => (
              <span
                key={correspondent.key}
                className="badge desk-correspondent-chip"
                title={correspondent.name}
              >
                <span className="desk-correspondent-chip__label">{correspondent.name}</span>
              </span>
            ))}
          </div>
        )}
        {tags.length > 0 && (
          <div className="desk-item__tags" aria-hidden="true">
            {tags.map((tag) => {
              if (pendingRemovalTag && pendingRemovalTag.docId === doc.id && pendingRemovalTag.tagId === tag.id) {
                return null;
              }
              const colorStyle = getTagColorStyle(tag.color);
              const pendingRemoval =
                pendingRemovalTag && pendingRemovalTag.docId === doc.id && pendingRemovalTag.tagId === tag.id;
              const tagClasses = ['badge', 'tag-chip', 'tag-chip--draggable'];
              if (pendingRemoval) tagClasses.push('tag-chip--tear-pending');
              return (
                <span
                  key={tag.id}
                  className={tagClasses.join(' ')}
                  style={colorStyle || undefined}
                  title={tag.label}
                  draggable
                  data-desk-tag-chip="true"
                  onPointerDownCapture={(event) => {
                    onDocTagPointerDown?.(event, doc, tag);
                  }}
                  onDragStart={(event) => onDocTagDragStart?.(event, doc, tag)}
                  onDrag={onDocTagDrag}
                  onDragEnd={(event) => onDocTagDragEnd?.(event)}
                >
                  <span className="tag-chip__label">{tag.label}</span>
                </span>
              );
            })}
          </div>
        )}
      </div>
    </div>
  );
};

export default React.memo(DesktopDocumentCard);
