const DIRECTORY_MIME_TYPE = 'application/x-directory';

export { DIRECTORY_MIME_TYPE };

/** Electron exposes the real filesystem path on File objects from native drag-drop. */
export function getElectronFilePath(file: globalThis.File): string | null {
  try {
    const fromWebUtils = window.electron?.getPathForFile?.(file);
    if (typeof fromWebUtils === 'string' && fromWebUtils.length > 0) {
      return fromWebUtils;
    }
  } catch {
    // Fall back to legacy Electron File.path when webUtils is unavailable.
  }
  const path = (file as globalThis.File & { path?: string }).path;
  return typeof path === 'string' && path.length > 0 ? path : null;
}

export function normalizePathKey(path: string): string {
  const normalized = path.replace(/\\/g, '/');
  return window.electron?.platform === 'win32' ? normalized.toLowerCase() : normalized;
}

function getWebkitRelativePath(file: globalThis.File): string | null {
  const rel = (file as globalThis.File & { webkitRelativePath?: string }).webkitRelativePath?.trim();
  return rel ? rel : null;
}

/**
 * Windows Explorer often expands a folder drop into many files, each with
 * webkitRelativePath like "Project/src/index.ts". Recover the folder root path.
 */
export function inferExpandedFolderDropRoot(files: globalThis.File[]): string | null {
  if (files.length === 0) return null;

  const roots = files.map((file) => {
    const path = getElectronFilePath(file);
    const rel = getWebkitRelativePath(file);
    if (!path || !rel) return null;

    const parts = rel.split(/[\\/]/).filter(Boolean);
    if (parts.length < 2) return null;

    const folderName = parts[0]!;
    const normalizedPath = path.replace(/\\/g, '/');
    const normalizedRel = rel.replace(/\\/g, '/');
    if (!normalizedPath.endsWith(normalizedRel)) return null;

    return path.slice(0, path.length - rel.length) + folderName;
  }).filter((value): value is string => !!value);

  if (roots.length !== files.length) return null;

  const rootKey = normalizePathKey(roots[0]!);
  return roots.every((root) => normalizePathKey(root) === rootKey) ? roots[0]! : null;
}

function isDirectoryDragItem(item: DataTransferItem): boolean {
  const entry = item.webkitGetAsEntry?.();
  return entry?.isDirectory === true;
}

function resolveDirectoryDropPath(item: DataTransferItem, allFiles: globalThis.File[]): string | null {
  const entry = item.webkitGetAsEntry?.();
  const file = item.getAsFile();
  const candidates = file
    ? [file]
    : entry?.name
      ? allFiles.filter((candidate) => candidate.name === entry.name)
      : [];

  for (const candidate of candidates) {
    const path = getElectronFilePath(candidate);
    if (path) return path;
  }
  return null;
}

export function collectDroppedFiles(dataTransfer: DataTransfer): {
  pathFiles: string[];
  bufferFiles: globalThis.File[];
} {
  const pathFiles: string[] = [];
  const bufferFiles: globalThis.File[] = [];
  const seenPaths = new Set<string>();
  const allFiles = Array.from(dataTransfer.files ?? []);

  const addPath = (path: string) => {
    const key = normalizePathKey(path);
    if (seenPaths.has(key)) return;
    seenPaths.add(key);
    pathFiles.push(path);
  };

  const expandedFolderRoot = inferExpandedFolderDropRoot(allFiles);
  if (expandedFolderRoot) {
    addPath(expandedFolderRoot);
    return { pathFiles, bufferFiles };
  }

  const items = dataTransfer.items ? Array.from(dataTransfer.items) : [];
  if (items.length > 0) {
    for (const item of items) {
      if (item.kind !== 'file') continue;

      if (isDirectoryDragItem(item)) {
        const directoryPath = resolveDirectoryDropPath(item, allFiles);
        if (directoryPath) addPath(directoryPath);
        continue;
      }

      const file = item.getAsFile();
      if (!file) continue;

      const electronPath = getElectronFilePath(file);
      if (electronPath) {
        addPath(electronPath);
        continue;
      }
      bufferFiles.push(file);
    }
    return { pathFiles, bufferFiles };
  }

  for (const file of allFiles) {
    const electronPath = getElectronFilePath(file);
    if (electronPath) {
      addPath(electronPath);
    } else {
      bufferFiles.push(file);
    }
  }
  return { pathFiles, bufferFiles };
}