import fs__default from 'node:fs';
import path__default from 'node:path';
import '../../../../../_virtual/index2.mjs';
import fastGlob from '../../../../../_virtual/index3.mjs';
import dirGlob from '../../../../../_virtual/index4.mjs';
import { isIgnoredByIgnoreFiles, GITIGNORE_FILES_PATTERN } from './ignore.mjs';
import { toPath, isNegativePattern } from './utilities.mjs';

const assertPatternsInput = patterns => {
  if (patterns.some(pattern => typeof pattern !== 'string')) {
    throw new TypeError('Patterns must be a string or an array of strings');
  }
};
const toPatternsArray = patterns => {
  patterns = [...new Set([patterns].flat())];
  assertPatternsInput(patterns);
  return patterns;
};
const checkCwdOption = options => {
  if (!options.cwd) {
    return;
  }
  let stat;
  try {
    stat = fs__default.statSync(options.cwd);
  } catch {
    return;
  }
  if (!stat.isDirectory()) {
    throw new Error('The `cwd` option must be a path to a directory');
  }
};
const normalizeOptions = (options = {}) => {
  options = {
    ...options,
    ignore: options.ignore || [],
    expandDirectories: options.expandDirectories === undefined ? true : options.expandDirectories,
    cwd: toPath(options.cwd)
  };
  checkCwdOption(options);
  return options;
};
const normalizeArguments = fn => async (patterns, options) => fn(toPatternsArray(patterns), normalizeOptions(options));
const getIgnoreFilesPatterns = options => {
  const {
    ignoreFiles,
    gitignore
  } = options;
  const patterns = ignoreFiles ? toPatternsArray(ignoreFiles) : [];
  if (gitignore) {
    patterns.push(GITIGNORE_FILES_PATTERN);
  }
  return patterns;
};
const getFilter = async options => {
  const ignoreFilesPatterns = getIgnoreFilesPatterns(options);
  return createFilterFunction(ignoreFilesPatterns.length > 0 && (await isIgnoredByIgnoreFiles(ignoreFilesPatterns, options)));
};
const createFilterFunction = isIgnored => {
  const seen = new Set();
  return fastGlobResult => {
    const path = fastGlobResult.path || fastGlobResult;
    const pathKey = path__default.normalize(path);
    const seenOrIgnored = seen.has(pathKey) || isIgnored && isIgnored(path);
    seen.add(pathKey);
    return !seenOrIgnored;
  };
};
const unionFastGlobResults = (results, filter) => results.flat().filter(fastGlobResult => filter(fastGlobResult));
const convertNegativePatterns = (patterns, options) => {
  const tasks = [];
  while (patterns.length > 0) {
    const index = patterns.findIndex(pattern => isNegativePattern(pattern));
    if (index === -1) {
      tasks.push({
        patterns,
        options
      });
      break;
    }
    const ignorePattern = patterns[index].slice(1);
    for (const task of tasks) {
      task.options.ignore.push(ignorePattern);
    }
    if (index !== 0) {
      tasks.push({
        patterns: patterns.slice(0, index),
        options: {
          ...options,
          ignore: [...options.ignore, ignorePattern]
        }
      });
    }
    patterns = patterns.slice(index + 1);
  }
  return tasks;
};
const getDirGlobOptions = (options, cwd) => ({
  ...(cwd ? {
    cwd
  } : {}),
  ...(Array.isArray(options) ? {
    files: options
  } : options)
});
const generateTasks = async (patterns, options) => {
  const globTasks = convertNegativePatterns(patterns, options);
  const {
    cwd,
    expandDirectories
  } = options;
  if (!expandDirectories) {
    return globTasks;
  }
  const patternExpandOptions = getDirGlobOptions(expandDirectories, cwd);
  const ignoreExpandOptions = cwd ? {
    cwd
  } : undefined;
  return Promise.all(globTasks.map(async task => {
    let {
      patterns,
      options
    } = task;
    [patterns, options.ignore] = await Promise.all([dirGlob(patterns, patternExpandOptions), dirGlob(options.ignore, ignoreExpandOptions)]);
    return {
      patterns,
      options
    };
  }));
};
const globby = normalizeArguments(async (patterns, options) => {
  const [tasks, filter] = await Promise.all([generateTasks(patterns, options), getFilter(options)]);
  const results = await Promise.all(tasks.map(task => fastGlob(task.patterns, task.options)));
  return unionFastGlobResults(results, filter);
});

export { globby };
