import React from 'react';
import type { DragEvent, MouseEvent, RefObject } from 'react';
import { FolderIcon, CheckIcon, CloseIcon } from '../ui/icons';
import DocumentThumbnailImage from './DocumentThumbnailImage';
import CorrespondentLinks from './CorrespondentLinks';
import { getTagColorStyle } from '../utils/colors';
import { resolveCorrespondents } from './correspondents';
import { writeTagTransferData } from './tagTransfer';
import useInlineRename from './useInlineRename';

export type Identifier = string | number;

export interface FolderLike {
  id?: Identifier | 'root';
  name?: string;
}

export interface DocumentTag {
  id?: Identifier;
  label?: string;
  color?: string | null;
}

export interface DocumentCorrespondent {
  id?: Identifier;
  name?: string;
  count?: number;
}

export interface DocumentLike {
  id?: Identifier;
  title?: string;
  tags?: DocumentTag[] | null;
  correspondents?: DocumentCorrespondent[] | null;
}

export type FolderEntry = {
  type: 'folder';
  id: Identifier | 'root';
  key: string;
  folder: FolderLike;
};

export type DocumentEntry = {
  type: 'document';
  id: Identifier;
  key: string;
  document: DocumentLike;
};

export type DocumentsGridEntry = FolderEntry | DocumentEntry;

type FolderEventHandler = (folder: FolderLike, event: MouseEvent<HTMLDivElement>) => void;
type DocumentEventHandler = (document: DocumentLike, event: MouseEvent<HTMLDivElement>) => void;

interface DocumentsGridProps {
  entries: DocumentsGridEntry[];
  selectedDocumentIdsSet?: Set<Identifier> | null;
  selectedFolderIdsSet?: Set<Identifier | 'root'> | null;
  draggingDocumentIdsSet?: Set<Identifier> | null;
  draggedFolderId?: Identifier | 'root' | null;
  onFolderClick?: FolderEventHandler;
  onFolderSelect?: (folderId: Identifier | 'root') => void;
  onFolderDragOver?: (event: DragEvent<HTMLDivElement>, folderId: Identifier | 'root') => void;
  onFolderDragLeave?: (event: DragEvent<HTMLDivElement>) => void;
  onFolderDrop?: (event: DragEvent<HTMLDivElement>, folderId: Identifier | 'root') => void;
  onFolderDragStart?: (event: DragEvent<HTMLDivElement>, folderId: Identifier | 'root') => void;
  onFolderDragEnd?: (event: DragEvent<HTMLDivElement>) => void;
  onFolderRename?: (folderId: Identifier | 'root', nextName: string) => Promise<boolean> | boolean;
  onDocumentClick?: DocumentEventHandler;
  onDocumentActivate?: DocumentEventHandler;
  onDocumentDragStart?: (event: DragEvent<HTMLDivElement>, document: DocumentLike) => void;
  onDocumentDragEnd?: (event: DragEvent<HTMLDivElement>) => void;
  onDocumentTagDragOver?: (event: DragEvent<HTMLDivElement>) => void;
  onDocumentTagDragLeave?: (event: DragEvent<HTMLDivElement>) => void;
  onDocumentTagDrop?: (event: DragEvent<HTMLDivElement>, documentId: Identifier) => void;
  ensureAssetUrl?: (...args: any[]) => unknown;
  getDocumentAsset?: (...args: any[]) => unknown;
  gridIconSize?: number;
  tagLookupById?: Map<Identifier, DocumentTag> | null;
  onTagClick?: (tagId?: Identifier | null) => void;
  scrollRef?: RefObject<HTMLElement | null>;
  onCorrespondentClick?: (correspondentId?: Identifier | null) => void;
  activeCorrespondentIdSet?: Set<Identifier | null | undefined> | null;
  onClearSelection?: () => void;
  onDocumentRename?: (docId: Identifier, title: string) => Promise<boolean> | boolean;
}

const DocumentsGrid: React.FC<DocumentsGridProps> = ({
  entries,
  selectedDocumentIdsSet,
  selectedFolderIdsSet,
  draggingDocumentIdsSet,
  draggedFolderId,
  onFolderClick,
  onFolderSelect,
  onFolderDragOver,
  onFolderDragLeave,
  onFolderDrop,
  onFolderDragStart,
  onFolderDragEnd,
  onDocumentClick,
  onDocumentActivate,
  onDocumentDragStart,
  onDocumentDragEnd,
  onDocumentTagDragOver,
  onDocumentTagDragLeave,
  onDocumentTagDrop,
  ensureAssetUrl,
  getDocumentAsset,
  gridIconSize,
  tagLookupById,
  onTagClick,
  scrollRef,
  onCorrespondentClick,
  activeCorrespondentIdSet,
  onClearSelection,
  onDocumentRename,
  onFolderRename,
}) => {
  const {
    editingId: editingDocumentId,
    draftValue: documentDraft,
    setDraftValue: setDocumentDraft,
    beginEditing: beginDocumentEditing,
    cancelEditing: cancelDocumentEditing,
    submitEditing: submitDocumentEditing,
    savingId: savingDocumentId,
    attachInputRef: attachDocumentInputRef,
  } = useInlineRename<DocumentLike>(onDocumentRename, {
    getCurrentValue: (doc: DocumentLike) => doc?.title ?? '',
    getEntityId: (doc: DocumentLike) => doc?.id ?? null,
  });

  const {
    editingId: editingFolderId,
    draftValue: folderDraft,
    setDraftValue: setFolderDraft,
    beginEditing: beginFolderEditing,
    cancelEditing: cancelFolderEditing,
    submitEditing: submitFolderEditing,
    savingId: savingFolderId,
    attachInputRef: attachFolderInputRef,
  } = useInlineRename<FolderLike>(onFolderRename, {
    getCurrentValue: (folder: FolderLike) => folder?.name ?? '',
    getEntityId: (folder: FolderLike) => folder?.id ?? null,
  });

  const documentSelectionCount = selectedDocumentIdsSet?.size ?? 0;
  const folderSelectionCount = selectedFolderIdsSet?.size ?? 0;
  const totalSelectionCount = documentSelectionCount + folderSelectionCount;

  return (
  <div
    className="documents-grid"
    role="list"
    style={{ '--documents-grid-icon-size': `${gridIconSize}px` }}
    onClick={(event) => {
      if (event.target === event.currentTarget) {
        onClearSelection?.();
      }
    }}
  >
    {entries.map((entry) => {
      if (entry.type === 'folder') {
        const folder = entry.folder;
        if (!folder) {
          return null;
        }
        const canDragFolder = folder.id !== 'root';
        const isDraggingFolder = draggedFolderId === folder.id;
        const isSelectedFolder = selectedFolderIdsSet?.has(folder.id);
        const classes = ['document-card', 'folder-card'];
        if (isDraggingFolder) classes.push('is-dragging');
        if (isSelectedFolder) classes.push('selected');
        const canRenameFolder = folder.id !== 'root' && typeof onFolderRename === 'function';
        const isFolderEditing = editingFolderId === folder.id;
        const folderDraftValue = isFolderEditing ? folderDraft : folder.name;
        const trimmedFolderDraft = isFolderEditing ? folderDraft.trim() : '';
        const isFolderSaving = savingFolderId === folder.id;
        const canSubmitFolder =
          isFolderEditing && trimmedFolderDraft.length > 0 && trimmedFolderDraft !== folder.name;
        const allowInlineFolderEdit = canRenameFolder && isSelectedFolder && totalSelectionCount === 1;

        return (
          <div
            key={entry.key}
            className={classes.join(' ')}
            role="listitem"
            id={`folder-card-${folder.id}`}
            draggable={canDragFolder}
            onClick={(event) => onFolderClick?.(folder, event)}
            onDoubleClick={(event) => {
              event.preventDefault();
              onFolderSelect?.(folder.id);
            }}
            onDragOver={(event) => onFolderDragOver?.(event, folder.id)}
            onDragLeave={onFolderDragLeave}
            onDrop={(event) => onFolderDrop?.(event, folder.id)}
            onDragStart={(event) => {
              if (canDragFolder) {
                onFolderDragStart?.(event, folder.id);
              }
            }}
            onDragEnd={(event) => {
              if (canDragFolder) {
                onFolderDragEnd?.(event);
              }
            }}
          >
            <div className="folder-card__icon">
              <FolderIcon className="folder-card__icon-svg" size={gridIconSize} />
            </div>
            <div className="folder-card__meta">
              {isFolderEditing ? (
                <div className="folder-card__edit doc-title-edit">
                  <input
                    type="text"
                    ref={attachFolderInputRef}
                    value={folderDraftValue}
                    onChange={(event) => setFolderDraft(event.target.value)}
                    onClick={(event) => event.stopPropagation()}
                    onKeyDown={(event) => {
                      if (event.key === 'Enter') {
                        event.preventDefault();
                        submitFolderEditing(folder);
                      } else if (event.key === 'Escape') {
                        event.preventDefault();
                        cancelFolderEditing(event);
                      }
                    }}
                    onBlur={(event) => {
                      const nextFocus = event.relatedTarget;
                      if (!nextFocus || !event.currentTarget.parentElement?.contains(nextFocus)) {
                        cancelFolderEditing();
                      }
                    }}
                  />
                  <button
                    type="button"
                    className="icon-button"
                    aria-label="Save name"
                    title="Save name"
                    disabled={!canSubmitFolder || isFolderSaving}
                    onClick={(event) => {
                      event.stopPropagation();
                      submitFolderEditing(folder);
                    }}
                  >
                    <CheckIcon />
                  </button>
                  <button
                    type="button"
                    className="icon-button"
                    aria-label="Cancel"
                    title="Cancel"
                    onClick={(event) => cancelFolderEditing(event)}
                  >
                    <CloseIcon />
                  </button>
                </div>
              ) : (
                <div className="folder-card__label-row">
                  <span
                    className="folder-card__name"
                    title={folder.name}
                    role={allowInlineFolderEdit ? 'button' : undefined}
                    tabIndex={allowInlineFolderEdit ? 0 : undefined}
                    onClick={(event) => {
                      if (!allowInlineFolderEdit) {
                        return;
                      }
                      event.preventDefault();
                      event.stopPropagation();
                      beginFolderEditing(folder);
                    }}
                    onKeyDown={(event) => {
                      if (!allowInlineFolderEdit) {
                        return;
                      }
                      if (event.key === 'Enter') {
                        event.preventDefault();
                        event.stopPropagation();
                        beginFolderEditing(folder);
                      }
                    }}
                  >
                    {folder.name}
                  </span>
                </div>
              )}
            </div>
          </div>
        );
      }

      const doc = entry.document;
      if (!doc) {
        return null;
      }

      const isSelected = selectedDocumentIdsSet?.has(doc.id);
      const isDraggingDoc = draggingDocumentIdsSet?.has(doc.id);
      const tagList = Array.isArray(doc.tags) ? doc.tags : [];
      const visibleTags = tagList.slice(0, 3);
      const remainingTagCount = tagList.length > 3 ? tagList.length - 3 : 0;
      const correspondents = resolveCorrespondents(doc);
      const cardClasses = ['document-card', 'document'];
      if (isSelected) cardClasses.push('selected');
      if (isDraggingDoc) cardClasses.push('is-dragging');
      const isEditingDoc = editingDocumentId === doc.id;
      const documentDraftValue = isEditingDoc ? documentDraft : doc.title;
      const trimmedDocumentDraft = isEditingDoc ? documentDraft.trim() : '';
      const isDocumentSaving = savingDocumentId === doc.id;
      const canSubmitDocument =
        isEditingDoc && trimmedDocumentDraft.length > 0 && trimmedDocumentDraft !== doc.title;
      const allowInlineDocumentEdit = onDocumentRename && isSelected && totalSelectionCount === 1;
      return (
        <div
          key={entry.key}
          className={cardClasses.join(' ')}
          role="listitem"
          id={`document-card-${doc.id}`}
          data-doc-id={doc.id}
          onClick={(event) => onDocumentClick?.(doc, event)}
          onDoubleClick={(event) => onDocumentActivate?.(doc, event)}
          draggable
          onDragStart={(event) => onDocumentDragStart?.(event, doc)}
          onDragEnd={(event) => onDocumentDragEnd?.(event)}
          onDragOver={(event) => onDocumentTagDragOver?.(event)}
          onDragOverCapture={(event) => onDocumentTagDragOver?.(event)}
          onDragLeave={onDocumentTagDragLeave}
          onDragLeaveCapture={onDocumentTagDragLeave}
          onDrop={(event) => onDocumentTagDrop?.(event, doc.id)}
          onDropCapture={(event) => onDocumentTagDrop?.(event, doc.id)}
        >
          <DocumentThumbnailImage
            document={doc}
            ensureAssetUrl={ensureAssetUrl}
            getDocumentAsset={getDocumentAsset}
            alt={`Thumbnail for ${doc.title}`}
            maxSize={gridIconSize}
            scrollRootRef={scrollRef}
          />
          <div className="document-card__meta">
            <div className="document-card__title" title={doc.title}>
              {correspondents.length > 0 ? (
                <span className="doc-correspondents">
                  <CorrespondentLinks
                    correspondents={correspondents}
                    activeCorrespondentIdSet={activeCorrespondentIdSet}
                    onCorrespondentClick={onCorrespondentClick}
                  />
                </span>
              ) : null}
              {isEditingDoc ? (
                <div className="document-card__title-edit doc-title-edit">
                  <input
                    type="text"
                    ref={attachDocumentInputRef}
                    value={documentDraftValue}
                    onChange={(event) => setDocumentDraft(event.target.value)}
                    onClick={(event) => event.stopPropagation()}
                    onKeyDown={(event) => {
                      if (event.key === 'Enter') {
                        event.preventDefault();
                        submitDocumentEditing(doc);
                      } else if (event.key === 'Escape') {
                        event.preventDefault();
                        cancelDocumentEditing(event);
                      }
                    }}
                    onBlur={(event) => {
                      const nextFocus = event.relatedTarget;
                      if (!nextFocus || !event.currentTarget.parentElement?.contains(nextFocus)) {
                        cancelDocumentEditing();
                      }
                    }}
                  />
                  <button
                    type="button"
                    className="icon-button"
                    aria-label="Save title"
                    title="Save title"
                    disabled={!canSubmitDocument || isDocumentSaving}
                    onClick={(event) => {
                      event.stopPropagation();
                      submitDocumentEditing(doc);
                    }}
                  >
                    <CheckIcon />
                  </button>
                  <button
                    type="button"
                    className="icon-button"
                    aria-label="Cancel"
                    title="Cancel"
                    onClick={(event) => cancelDocumentEditing(event)}
                  >
                    <CloseIcon />
                  </button>
                </div>
              ) : (
                <div className="document-card__title-row">
                  <span
                    className="document-card__title-badge"
                    role={allowInlineDocumentEdit ? 'button' : undefined}
                    tabIndex={allowInlineDocumentEdit ? 0 : undefined}
                    onClick={(event) => {
                      if (!allowInlineDocumentEdit) {
                        return;
                      }
                      event.preventDefault();
                      event.stopPropagation();
                      beginDocumentEditing(doc);
                    }}
                    onKeyDown={(event) => {
                      if (!allowInlineDocumentEdit) {
                        return;
                      }
                      if (event.key === 'Enter') {
                        event.preventDefault();
                        event.stopPropagation();
                        beginDocumentEditing(doc);
                      }
                    }}
                  >
                    {doc.title}
                  </span>
                </div>
              )}
            </div>
            {visibleTags.length > 0 && (
              <div className="document-card__tags">
                {visibleTags.map((tag) => {
                  const colorSource = tag?.color || tagLookupById?.get(tag.id)?.color;
                  const style = getTagColorStyle(colorSource);
                  return (
                    <span
                      key={tag.id}
                      className="badge tag-chip"
                      style={style || undefined}
                      title={tag.label}
                      role="button"
                      onClick={(event) => {
                        event.stopPropagation();
                        onTagClick?.(tag.id);
                      }}
                      draggable
                      onDragStart={(event) => {
                        event.stopPropagation();
                        try {
                          if (event.dataTransfer) {
                            event.dataTransfer.effectAllowed = 'copyMove';
                          }
                        } catch (error) {
                          console.warn('[documents] Failed to configure drag effect', error);
                        }
                        writeTagTransferData(event.dataTransfer, tag, doc.id);
                      }}
                      onDragEnd={(event) => {
                        event.stopPropagation();
                      }}
                      onKeyDown={(event) => {
                        if (event.key === 'Enter' || event.key === ' ') {
                          event.preventDefault();
                          event.stopPropagation();
                          onTagClick?.(tag.id);
                        }
                      }}
                    >
                      {tag.label}
                    </span>
                  );
                })}
                {remainingTagCount > 0 && (
                  <span className="badge tag-chip tag-chip--more">+{remainingTagCount}</span>
                )}
              </div>
            )}
          </div>
        </div>
      );
    })}
  </div>
  );
};

export default DocumentsGrid;
