import React, { useEffect, useRef, useState } from 'react';
import { convertFileSrc } from '@tauri-apps/api/core';
import { openPath } from '@tauri-apps/plugin-opener';
import { Eye, File, FileText, Image, Plus, Upload, X } from 'lucide-react';
import { researchApi } from '../../../research/researchApi';
import { extractResearchFileText } from '../../../lib/researchFileExtraction';
import type {
  ResearchFile,
  ResearchFilePreviewKind,
  ResearchFileTextPreview,
  UploadResearchFileInput,
} from '../../../types/research';

export const MAX_RESEARCH_UPLOAD_BYTES = 25 * 1024 * 1024;

export const readUploadInputs = async (files: FileList | File[]) => {
  const selectedFiles = Array.from(files);
  const oversized = selectedFiles.find((file) => file.size > MAX_RESEARCH_UPLOAD_BYTES);
  if (oversized) {
    throw new Error(`${oversized.name} exceeds the 25 MB max upload size.`);
  }

  const inputs: UploadResearchFileInput[] = [];
  for (const file of selectedFiles) {
    const [extraction, arrayBuffer] = await Promise.all([
      extractResearchFileText(file),
      file.arrayBuffer(),
    ]);
    const bytes = Array.from(new Uint8Array(arrayBuffer));
    inputs.push({
      name: file.name,
      mimeType: file.type || undefined,
      bytes,
      ...extraction,
    });
  }
  return inputs;
};

export const formatFileSize = (sizeBytes: number) => {
  if (sizeBytes < 1024) return `${sizeBytes} B`;
  if (sizeBytes < 1024 * 1024) return `${Math.round(sizeBytes / 1024)} KB`;
  return `${(sizeBytes / (1024 * 1024)).toFixed(sizeBytes >= 10 * 1024 * 1024 ? 0 : 1)} MB`;
};

const previewIcon = (kind: ResearchFilePreviewKind) => {
  if (kind === 'image') return <Image className="h-3.5 w-3.5" />;
  if (kind === 'pdf' || kind === 'text') return <FileText className="h-3.5 w-3.5" />;
  return <File className="h-3.5 w-3.5" />;
};

interface ResearchFileListProps {
  files: ResearchFile[];
  linkedNoteCountByFileId?: Record<string, number>;
  selectedFileIds?: string[];
  emptyLabel?: string;
  compact?: boolean;
  onOpenFile?: (file: ResearchFile) => Promise<void> | void;
  onPreviewFile?: (file: ResearchFile) => void;
  onRemoveFile?: (fileId: string) => void;
}

export const ResearchFileList: React.FC<ResearchFileListProps> = ({
  files,
  linkedNoteCountByFileId = {},
  selectedFileIds = [],
  emptyLabel = 'No files',
  compact = false,
  onOpenFile,
  onPreviewFile,
  onRemoveFile,
}) => {
  const [openError, setOpenError] = useState<string | null>(null);

  if (files.length === 0) {
    return (
      <div className="rounded-2xl border border-dashed border-term-border bg-term-bg px-3 py-4 text-center text-xs text-term-text-tertiary">
        {emptyLabel}
      </div>
    );
  }

  const selected = new Set(selectedFileIds);

  return (
    <div className="space-y-2">
      {openError ? (
        <div className="rounded-xl border border-negative/40 bg-negative/10 px-3 py-2 text-xs text-negative">
          {openError}
        </div>
      ) : null}
      <ul className="space-y-2">
        {files.map((file) => {
          const linkedNoteCount = linkedNoteCountByFileId[file.id] ?? 0;
          return (
            <li
              key={file.id}
              className={`rounded-2xl border bg-term-bg transition-colors ${
                selected.has(file.id) ? 'border-info/60' : 'border-term-border'
              } ${compact ? 'p-2' : 'p-3'}`}
            >
              <div className="flex items-start gap-2.5">
                <div className="mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-lg border border-term-border bg-term-surface text-term-text-tertiary">
                  {previewIcon(file.previewKind)}
                </div>
                <button
                  type="button"
                  onClick={() => {
                    setOpenError(null);
                    void Promise.resolve(onOpenFile?.(file)).catch((error) => {
                      setOpenError(error instanceof Error ? error.message : String(error));
                    });
                  }}
                  className="min-w-0 flex-1 text-left"
                  title="Open with default app"
                >
                  <div className="truncate text-sm font-medium text-term-text">
                    {file.originalName}
                  </div>
                  <div className="mt-1 flex flex-wrap gap-2 text-[10px] uppercase tracking-[0.12em] text-term-text-tertiary">
                    <span>{formatFileSize(file.sizeBytes)}</span>
                    {linkedNoteCount > 0 ? <span>{linkedNoteCount} notes</span> : null}
                  </div>
                </button>
                <div className="flex shrink-0 gap-1">
                  <button
                    type="button"
                    onClick={() => onPreviewFile?.(file)}
                    className="rounded-lg border border-term-border bg-term-surface p-1.5 text-term-text-tertiary transition-colors hover:border-info/60 hover:text-term-text"
                    title="Preview"
                  >
                    <Eye className="h-3.5 w-3.5" />
                  </button>
                  {onRemoveFile ? (
                    <button
                      type="button"
                      onClick={() => onRemoveFile(file.id)}
                      className="rounded-lg border border-term-border bg-term-surface p-1.5 text-term-text-tertiary transition-colors hover:border-negative/60 hover:text-negative"
                      title="Remove"
                    >
                      <X className="h-3.5 w-3.5" />
                    </button>
                  ) : null}
                </div>
              </div>
            </li>
          );
        })}
      </ul>
    </div>
  );
};

interface ResearchFileUploadButtonProps {
  disabled?: boolean;
  label?: string;
  onUpload: (files: UploadResearchFileInput[]) => Promise<unknown> | unknown;
  onError?: (message: string) => void;
}

export const ResearchFileUploadButton: React.FC<ResearchFileUploadButtonProps> = ({
  disabled = false,
  label = 'Upload Files',
  onUpload,
  onError,
}) => {
  const inputRef = useRef<HTMLInputElement>(null);

  return (
    <>
      <input
        ref={inputRef}
        type="file"
        multiple
        className="hidden"
        onChange={(event) => {
          const files = event.target.files ? Array.from(event.target.files) : [];
          event.target.value = '';
          if (files.length === 0) return;
          void readUploadInputs(files)
            .then((inputs) => onUpload(inputs))
            .catch((error) => {
              onError?.(error instanceof Error ? error.message : String(error));
            });
        }}
      />
      <button
        type="button"
        disabled={disabled}
        onClick={() => inputRef.current?.click()}
        className="inline-flex items-center gap-2 rounded-xl border border-info/40 bg-info/10 px-3 py-2 text-xs uppercase tracking-[0.16em] text-info transition-all hover:border-info hover:bg-info/20 hover:text-term-text hover:shadow-md hover:shadow-info/10 disabled:cursor-not-allowed disabled:opacity-50"
      >
        <Upload className="h-3.5 w-3.5" />
        {label}
      </button>
    </>
  );
};

interface ResearchFilePickerModalProps {
  isOpen: boolean;
  files: ResearchFile[];
  selectedFileIds: string[];
  onClose: () => void;
  onConfirm: (fileIds: string[]) => void;
  onOpenFile: (file: ResearchFile) => Promise<void> | void;
  onPreviewFile: (file: ResearchFile) => void;
}

export const ResearchFilePickerModal: React.FC<ResearchFilePickerModalProps> = ({
  isOpen,
  files,
  selectedFileIds,
  onClose,
  onConfirm,
  onOpenFile,
  onPreviewFile,
}) => {
  const [draftIds, setDraftIds] = useState<string[]>(selectedFileIds);

  useEffect(() => {
    if (isOpen) {
      setDraftIds(selectedFileIds);
    }
  }, [isOpen, selectedFileIds]);

  if (!isOpen) return null;

  const selected = new Set(draftIds);
  const toggle = (fileId: string) => {
    setDraftIds((current) =>
      current.includes(fileId)
        ? current.filter((id) => id !== fileId)
        : [...current, fileId],
    );
  };

  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4" role="dialog" aria-modal="true">
      <div className="flex max-h-[86vh] w-full max-w-2xl flex-col overflow-hidden rounded-[24px] border border-term-border bg-term-elevated shadow-2xl">
        <div className="flex items-center justify-between border-b border-term-border bg-term-surface px-5 py-4">
          <h2 className="text-base font-semibold text-term-text">Link Existing Files</h2>
          <button type="button" onClick={onClose} className="text-term-text-tertiary hover:text-term-text" aria-label="Close">
            <X className="h-5 w-5" />
          </button>
        </div>
        <div className="min-h-0 flex-1 overflow-y-auto p-5">
          {files.length === 0 ? (
            <div className="rounded-2xl border border-dashed border-term-border bg-term-bg p-6 text-center text-sm text-term-text-muted">
              No workspace files
            </div>
          ) : (
            <ul className="space-y-2">
              {files.map((file) => (
                <li key={file.id} className="rounded-2xl border border-term-border bg-term-bg p-3">
                  <div className="flex items-center gap-3">
                    <input
                      type="checkbox"
                      checked={selected.has(file.id)}
                      onChange={() => toggle(file.id)}
                      className="h-4 w-4 accent-info"
                    />
                    <button type="button" onClick={() => toggle(file.id)} className="min-w-0 flex-1 text-left">
                      <div className="truncate text-sm font-medium text-term-text">{file.originalName}</div>
                      <div className="mt-1 text-xs text-term-text-tertiary">{formatFileSize(file.sizeBytes)}</div>
                    </button>
                    <button type="button" onClick={() => onPreviewFile(file)} className="rounded-lg border border-term-border p-1.5 text-term-text-tertiary hover:border-info/60 hover:text-term-text" title="Preview">
                      <Eye className="h-3.5 w-3.5" />
                    </button>
                    <button type="button" onClick={() => onOpenFile(file)} className="rounded-lg border border-term-border p-1.5 text-term-text-tertiary hover:border-info/60 hover:text-term-text" title="Open">
                      <File className="h-3.5 w-3.5" />
                    </button>
                  </div>
                </li>
              ))}
            </ul>
          )}
        </div>
        <div className="flex items-center justify-end gap-2 border-t border-term-border bg-term-surface px-5 py-4">
          <button type="button" onClick={onClose} className="rounded-xl border border-term-border bg-term-bg px-4 py-2 text-sm text-term-text hover:border-info/60">
            Cancel
          </button>
          <button type="button" onClick={() => onConfirm(draftIds)} className="inline-flex items-center gap-2 rounded-xl border border-info bg-info px-4 py-2 text-sm font-semibold text-term-bg hover:brightness-110">
            <Plus className="h-4 w-4" />
            Link
          </button>
        </div>
      </div>
    </div>
  );
};

interface ResearchFilePreviewModalProps {
  file: ResearchFile | null;
  onClose: () => void;
}

export const ResearchFilePreviewModal: React.FC<ResearchFilePreviewModalProps> = ({
  file,
  onClose,
}) => {
  const [textPreview, setTextPreview] = useState<ResearchFileTextPreview | null>(null);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    setTextPreview(null);
    setError(null);
    if (!file || file.previewKind !== 'text') return;
    let cancelled = false;
    void researchApi
      .getTextPreview({ fileId: file.id })
      .then((preview) => {
        if (!cancelled) setTextPreview(preview);
      })
      .catch((previewError) => {
        if (!cancelled) {
          setError(previewError instanceof Error ? previewError.message : String(previewError));
        }
      });
    return () => {
      cancelled = true;
    };
  }, [file]);

  if (!file) return null;

  const assetSrc = file.previewKind === 'image' || file.previewKind === 'pdf'
    ? convertFileSrc(file.storedPath)
    : null;

  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/70 p-4" role="dialog" aria-modal="true">
      <div className="flex max-h-[90vh] w-full max-w-5xl flex-col overflow-hidden rounded-[24px] border border-term-border bg-term-elevated shadow-2xl">
        <div className="flex items-center justify-between border-b border-term-border bg-term-surface px-5 py-4">
          <div className="min-w-0">
            <h2 className="truncate text-base font-semibold text-term-text">{file.originalName}</h2>
            <div className="mt-1 text-xs text-term-text-tertiary">{formatFileSize(file.sizeBytes)}</div>
          </div>
          <div className="flex items-center gap-2">
            <button type="button" onClick={() => void openPath(file.storedPath).catch((openError) => setError(openError instanceof Error ? openError.message : String(openError)))} className="rounded-xl border border-term-border bg-term-bg px-3 py-2 text-xs uppercase tracking-[0.16em] text-term-text-tertiary hover:border-info/60 hover:text-term-text">
              Open
            </button>
            <button type="button" onClick={onClose} className="text-term-text-tertiary hover:text-term-text" aria-label="Close">
              <X className="h-5 w-5" />
            </button>
          </div>
        </div>
        {error ? (
          <div className="border-b border-negative/30 bg-negative/10 px-5 py-2 text-sm text-negative">
            {error}
          </div>
        ) : null}
        <div className="min-h-0 flex-1 overflow-auto bg-term-bg p-5">
          {file.previewKind === 'image' && assetSrc ? (
            <img src={assetSrc} alt={file.originalName} className="mx-auto max-h-[70vh] max-w-full object-contain" />
          ) : file.previewKind === 'pdf' && assetSrc ? (
            <iframe title={file.originalName} src={assetSrc} className="h-[72vh] w-full rounded-xl border border-term-border bg-white" />
          ) : file.previewKind === 'text' ? (
            <pre className="min-h-[50vh] overflow-auto whitespace-pre-wrap rounded-xl border border-term-border bg-term-elevated p-4 text-xs leading-6 text-term-text">
              {textPreview?.text ?? 'Loading...'}
              {textPreview?.truncated ? '\n\n[Preview truncated]' : ''}
            </pre>
          ) : (
            <div className="rounded-2xl border border-term-border bg-term-elevated p-6 text-sm text-term-text-muted">
              Inline preview is not available for this file type.
            </div>
          )}
        </div>
      </div>
    </div>
  );
};

export const openResearchFile = async (file: ResearchFile) => {
  await openPath(file.storedPath);
};

export const buildLinkedNoteCountByFileId = (
  notes: Array<{ fileIds?: string[] }>,
): Record<string, number> => {
  const counts: Record<string, number> = {};
  notes.forEach((note) => {
    (note.fileIds ?? []).forEach((fileId) => {
      counts[fileId] = (counts[fileId] ?? 0) + 1;
    });
  });
  return counts;
};

export const sortResearchFilesNewestFirst = (files: ResearchFile[]) =>
  [...files].sort((left, right) =>
    String(right.createdAt).localeCompare(String(left.createdAt)),
  );

export const filterResearchFilesByIds = (files: ResearchFile[], fileIds: string[]) => {
  const fileById = new Map(files.map((file) => [file.id, file]));
  return fileIds.map((fileId) => fileById.get(fileId)).filter((file): file is ResearchFile => Boolean(file));
};
