/**
 * 검증 이슈 첨부 — 화면 캡처·파일 선택·드래그·클립보드 붙여넣기
 * 이미지 + zip·hwp·hwpx
 */
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import ScreenRegionCaptureOverlay from '../ScreenRegionCaptureOverlay';
import {
  deleteVerificationIssueImage,
  loadVerificationIssueImages,
  uploadVerificationIssueImages,
  verificationIssueImageUrl,
  type VerificationIssueImageDto,
} from '../../utils/verificationBoardApi';
import {
  filesFromClipboardRead,
  filesFromDataTransfer,
} from '../../utils/clipboardImageFiles';
import {
  ATTACHMENT_TYPE_HINT,
  VERIFICATION_ATTACHMENT_ACCEPT,
  isImageAttachment,
  pickAttachmentFiles,
  prepareAttachmentsForUpload,
} from '../../utils/verificationAttachmentFiles';

function uploadErrorMessage(e: unknown): string {
  if (e instanceof Error) {
    if (e.message === 'IMAGE_TOO_LARGE' || e.message === 'FILE_TOO_LARGE') {
      return '파일이 너무 큽니다. 5MB 이하로 줄인 뒤 다시 시도하세요.';
    }
    if (e.message === 'IMAGE_READ_FAILED') {
      return '이미지를 읽을 수 없습니다.';
    }
    if (e.message) return e.message;
  }
  return '첨부 파일 업로드에 실패했습니다.';
}

function fileKindLabel(fileName?: string | null, contentType?: string | null): string {
  const lower = (fileName ?? '').toLowerCase();
  if (lower.endsWith('.zip')) return 'ZIP';
  if (lower.endsWith('.hwp')) return 'HWP';
  if (lower.endsWith('.hwpx')) return 'HWPX';
  const mime = (contentType ?? '').toLowerCase();
  if (mime.includes('zip')) return 'ZIP';
  if (mime.includes('hwp')) return 'HWP';
  return 'FILE';
}

interface Props {
  /** 저장된 이슈 ID — 수정·상세 화면 */
  issueId?: number | null;
  /** 등록 draft — 저장 전 로컬 파일 */
  draftFiles?: File[];
  onDraftFilesChange?: (files: File[]) => void;
  /** true: 새 첨부 업로드만 저장 시 반영 (삭제는 즉시 API) */
  deferUpload?: boolean;
  disabled?: boolean;
  /** compact: 모달 등 좁은 영역 */
  compact?: boolean;
  /** 첨부 추가·삭제 후 (목록 갱신 등) */
  onAttachmentsChanged?: () => void;
}

interface CtxMenuState {
  x: number;
  y: number;
}

const VerificationIssueAttachmentsSection: React.FC<Props> = ({
  issueId = null,
  draftFiles = [],
  onDraftFilesChange,
  deferUpload = false,
  disabled = false,
  compact = false,
  onAttachmentsChanged,
}) => {
  const isDraft = issueId == null;
  const saveOnSubmit = deferUpload && issueId != null;
  const [images, setImages] = useState<VerificationIssueImageDto[]>([]);
  const [loading, setLoading] = useState(false);
  const [uploading, setUploading] = useState(false);
  const [dragOver, setDragOver] = useState(false);
  const [ctxMenu, setCtxMenu] = useState<CtxMenuState | null>(null);
  const [screenCaptureOpen, setScreenCaptureOpen] = useState(false);
  const fileInputRef = useRef<HTMLInputElement>(null);
  const zoneRef = useRef<HTMLDivElement>(null);

  const reload = useCallback(async () => {
    if (issueId == null) return;
    setLoading(true);
    try {
      setImages(await loadVerificationIssueImages(issueId));
    } catch (e) {
      console.warn('[VerificationBoard] images load', e);
    } finally {
      setLoading(false);
    }
  }, [issueId]);

  useEffect(() => {
    if (issueId != null) void reload();
    else setImages([]);
  }, [issueId, reload]);

  const draftPreviews = useMemo(
    () => draftFiles.map((file, index) => {
      const isImage = isImageAttachment(file.type, file.name);
      return {
        key: `${file.name}-${file.size}-${file.lastModified}-${index}`,
        index,
        fileName: file.name,
        isImage,
        src: isImage ? URL.createObjectURL(file) : undefined,
        kind: fileKindLabel(file.name, file.type),
      };
    }),
    [draftFiles],
  );

  useEffect(() => () => {
    draftPreviews.forEach(p => {
      if (p.src) URL.revokeObjectURL(p.src);
    });
  }, [draftPreviews]);

  useEffect(() => {
    const close = () => setCtxMenu(null);
    window.addEventListener('click', close);
    window.addEventListener('scroll', close, true);
    return () => {
      window.removeEventListener('click', close);
      window.removeEventListener('scroll', close, true);
    };
  }, []);

  const addDraftFiles = useCallback(async (files: File[]) => {
    const picked = pickAttachmentFiles(files);
    if (picked.length === 0) {
      window.alert(`첨부 가능: ${ATTACHMENT_TYPE_HINT}`);
      return;
    }
    setUploading(true);
    try {
      const prepared = await prepareAttachmentsForUpload(picked);
      onDraftFilesChange?.([...draftFiles, ...prepared]);
    } catch (e) {
      window.alert(uploadErrorMessage(e));
    } finally {
      setUploading(false);
    }
  }, [draftFiles, onDraftFilesChange]);

  const uploadFiles = useCallback(async (files: File[]) => {
    if (issueId == null || saveOnSubmit) {
      await addDraftFiles(files);
      return;
    }
    const picked = pickAttachmentFiles(files);
    if (picked.length === 0) {
      window.alert(`첨부 가능: ${ATTACHMENT_TYPE_HINT}`);
      return;
    }
    setUploading(true);
    try {
      const prepared = await prepareAttachmentsForUpload(picked);
      await uploadVerificationIssueImages(issueId, prepared);
      await reload();
      onAttachmentsChanged?.();
    } catch (e) {
      console.warn('[VerificationBoard] attachment upload', e);
      window.alert(uploadErrorMessage(e));
    } finally {
      setUploading(false);
    }
  }, [issueId, saveOnSubmit, addDraftFiles, reload]);

  const pasteFromClipboard = useCallback(async () => {
    setCtxMenu(null);
    try {
      const files = await filesFromClipboardRead();
      if (files.length === 0) {
        window.alert('클립보드에 붙여넣을 수 있는 이미지(png, jpeg 등)가 없습니다.');
        return;
      }
      await uploadFiles(files);
    } catch (e) {
      if (e instanceof Error && e.message === 'CLIPBOARD_UNSUPPORTED') {
        window.alert('이 브라우저에서는 클립보드 이미지 읽기를 지원하지 않습니다.');
        return;
      }
      window.alert('클립보드 접근이 거부되었습니다. 브라우저 권한을 확인하세요.');
    }
  }, [uploadFiles]);

  const handleScreenCapture = useCallback((file: File) => {
    setScreenCaptureOpen(false);
    void uploadFiles([file]);
  }, [uploadFiles]);

  const handleDeleteServer = async (imageId: number) => {
    if (issueId == null) return;
    if (!window.confirm('이 첨부 파일을 삭제할까요?')) return;
    setUploading(true);
    try {
      await deleteVerificationIssueImage(issueId, imageId);
      await reload();
      onAttachmentsChanged?.();
    } catch (e) {
      console.warn('[VerificationBoard] attachment delete', e);
      window.alert('첨부 파일 삭제에 실패했습니다.');
    } finally {
      setUploading(false);
    }
  };

  const handleDeleteDraft = (index: number) => {
    onDraftFilesChange?.(draftFiles.filter((_, i) => i !== index));
  };

  const openAttachment = (src: string, fileName: string, isImage: boolean) => {
    if (isImage) {
      window.open(src, '_blank', 'noopener,noreferrer');
      return;
    }
    const a = document.createElement('a');
    a.href = src;
    a.download = fileName;
    a.rel = 'noopener noreferrer';
    document.body.appendChild(a);
    a.click();
    a.remove();
  };

  const renderAttachmentThumb = (
    src: string,
    fileName: string,
    isImage: boolean,
    kind: string,
    onOpen: () => void,
  ) => (
    <button
      type="button"
      className={`vbd-attach-thumb${isImage ? '' : ' vbd-attach-thumb--file'}`}
      title={`${fileName} — 더블클릭하여 ${isImage ? '열기' : '다운로드'}`}
      onDoubleClick={onOpen}
    >
      {isImage ? (
        <img src={src} alt={fileName} loading="lazy" draggable={false} />
      ) : (
        <span className="vbd-attach-file-badge" aria-hidden>{kind}</span>
      )}
    </button>
  );

  const handleContextMenu = (e: React.MouseEvent) => {
    if (disabled) return;
    e.preventDefault();
    e.stopPropagation();
    setCtxMenu({ x: e.clientX, y: e.clientY });
  };

  const handlePaste = (e: React.ClipboardEvent) => {
    if (disabled) return;
    const fromItems = filesFromDataTransfer(e.clipboardData);
    const fromFiles = pickAttachmentFiles(e.clipboardData.files);
    const merged = fromFiles.length > 0 ? fromFiles : fromItems;
    if (merged.length === 0) return;
    e.preventDefault();
    void uploadFiles(merged);
  };

  const handleDrop = (e: React.DragEvent) => {
    if (disabled) return;
    e.preventDefault();
    setDragOver(false);
    const picked = pickAttachmentFiles(e.dataTransfer.files);
    if (picked.length > 0) void uploadFiles(picked);
  };

  const visibleServerImages = useMemo(
    () => images.filter(img => img.id != null),
    [images],
  );

  const totalCount = isDraft || saveOnSubmit
    ? visibleServerImages.length + draftFiles.length
    : images.length;
  const showEmptyHint = !loading && totalCount === 0;

  return (
    <div className={`vbd-attachments${compact ? ' vbd-attachments--compact' : ''}`}>
      <div className="vbd-attachments-head">
        <h3 className="vbd-attachments-title">
          첨부 파일 {totalCount > 0 && `(${totalCount})`}
        </h3>
        <div className="vbd-attachments-actions">
          <button
            type="button"
            className="vbd-btn vbd-btn--sm"
            title="화면 공유 후 드래그로 영역 캡처"
            onClick={() => setScreenCaptureOpen(true)}
            disabled={disabled || uploading}
          >
            화면 캡처
          </button>
          <button
            type="button"
            className="vbd-btn vbd-btn--sm"
            onClick={() => fileInputRef.current?.click()}
            disabled={disabled || uploading}
          >
            파일 선택
          </button>
        </div>
        <input
          ref={fileInputRef}
          type="file"
          accept={VERIFICATION_ATTACHMENT_ACCEPT}
          multiple
          hidden
          disabled={disabled}
          onChange={e => {
            const files = e.target.files;
            if (files?.length) void uploadFiles(pickAttachmentFiles(files));
            e.target.value = '';
          }}
        />
      </div>

      <div
        ref={zoneRef}
        className={`vbd-attach-zone${dragOver ? ' vbd-attach-zone--over' : ''}${disabled ? ' vbd-attach-zone--disabled' : ''}`}
        tabIndex={disabled ? -1 : 0}
        role="region"
        aria-label="첨부 파일 영역"
        onContextMenu={handleContextMenu}
        onPaste={handlePaste}
        onDragEnter={e => { if (disabled) return; e.preventDefault(); setDragOver(true); }}
        onDragOver={e => { if (disabled) return; e.preventDefault(); setDragOver(true); }}
        onDragLeave={e => {
          if (zoneRef.current && !zoneRef.current.contains(e.relatedTarget as Node)) {
            setDragOver(false);
          }
        }}
        onDrop={handleDrop}
      >
        {loading && issueId != null && !isDraft && images.length === 0 ? (
          <p className="vbd-attach-hint">첨부 파일 불러오는 중…</p>
        ) : showEmptyHint ? (
          <p className="vbd-attach-hint">
            {isDraft || saveOnSubmit
              ? '저장 시 함께 반영됩니다. 화면 캡처·드래그·우클릭 붙여넣기·파일 선택'
              : '화면 캡처·드래그·우클릭 붙여넣기·파일 선택'}
          </p>
        ) : (
          <div className="vbd-attach-grid">
            {(isDraft || saveOnSubmit) && visibleServerImages.map(img => {
                if (img.id == null) return null;
                const src = verificationIssueImageUrl(issueId!, img.id);
                const isImage = isImageAttachment(img.contentType, img.fileName);
                const kind = fileKindLabel(img.fileName, img.contentType);
                return (
                  <figure key={img.id} className="vbd-attach-item">
                    {renderAttachmentThumb(
                      src,
                      img.fileName ?? '첨부 파일',
                      isImage,
                      kind,
                      () => openAttachment(src, img.fileName ?? '첨부 파일', isImage),
                    )}
                    <figcaption className="vbd-attach-caption" title={img.fileName}>
                      {img.fileName}
                    </figcaption>
                    <button
                      type="button"
                      className="vbd-attach-remove"
                      title="삭제"
                      disabled={disabled}
                      onClick={() => void handleDeleteServer(img.id!)}
                    >
                      ✕
                    </button>
                  </figure>
                );
              })}
            {(isDraft || saveOnSubmit)
              ? draftPreviews.map(p => (
                <figure key={p.key} className="vbd-attach-item">
                  {p.isImage && p.src
                    ? renderAttachmentThumb(
                      p.src,
                      p.fileName,
                      true,
                      p.kind,
                      () => openAttachment(p.src!, p.fileName, true),
                    )
                    : (
                      <div className="vbd-attach-thumb vbd-attach-thumb--file">
                        <span className="vbd-attach-file-badge" aria-hidden>{p.kind}</span>
                      </div>
                    )}
                  <figcaption className="vbd-attach-caption" title={p.fileName}>
                    {p.fileName}
                  </figcaption>
                  <button
                    type="button"
                    className="vbd-attach-remove"
                    title="삭제"
                    disabled={disabled}
                    onClick={() => handleDeleteDraft(p.index)}
                  >
                    ✕
                  </button>
                </figure>
              ))
              : !saveOnSubmit && images.map(img => {
                if (img.id == null) return null;
                const src = verificationIssueImageUrl(issueId!, img.id);
                const isImage = isImageAttachment(img.contentType, img.fileName);
                const kind = fileKindLabel(img.fileName, img.contentType);
                return (
                  <figure key={img.id} className="vbd-attach-item">
                    {renderAttachmentThumb(
                      src,
                      img.fileName ?? '첨부 파일',
                      isImage,
                      kind,
                      () => openAttachment(src, img.fileName ?? '첨부 파일', isImage),
                    )}
                    <figcaption className="vbd-attach-caption" title={img.fileName}>
                      {img.fileName}
                    </figcaption>
                    <button
                      type="button"
                      className="vbd-attach-remove"
                      title="삭제"
                      disabled={disabled}
                      onClick={() => void handleDeleteServer(img.id!)}
                    >
                      ✕
                    </button>
                  </figure>
                );
              })}
          </div>
        )}
        {uploading && <div className="vbd-attach-uploading">업로드 중…</div>}
      </div>

      {ctxMenu && (
        <div
          className="vbd-attach-ctx"
          style={{ left: ctxMenu.x, top: ctxMenu.y }}
          onClick={e => e.stopPropagation()}
        >
          <button type="button" onClick={() => { setCtxMenu(null); fileInputRef.current?.click(); }}>
            파일에서 선택…
          </button>
          <button type="button" onClick={() => void pasteFromClipboard()}>
            클립보드 이미지 붙여넣기
          </button>
          <button type="button" onClick={() => { setCtxMenu(null); setScreenCaptureOpen(true); }}>
            화면 영역 캡처…
          </button>
        </div>
      )}

      {screenCaptureOpen && (
        <ScreenRegionCaptureOverlay
          onCapture={handleScreenCapture}
          onCancel={() => setScreenCaptureOpen(false)}
        />
      )}
    </div>
  );
};

export default VerificationIssueAttachmentsSection;
