import { promises } from 'fs';
import { prepareDirectoryForExport } from '../../export/helpers/prepare.mjs';
import { getGitHubRepoHash } from './hash.mjs';
import { downloadFile } from '../api/download.mjs';
import { unzip } from '../helpers/unzip.mjs';
import 'pathe';
import '../api/index.mjs';
import '../api/cache.mjs';
import 'crypto';
import '../../misc/scan.mjs';
import '../api/config.mjs';
import '../api/fetch.mjs';
import 'fs/promises';
import 'extract-zip';

async function findMatchingDirs(rootDir, hash) {
  const matches = [];
  const files = await promises.readdir(rootDir);
  for (let i = 0; i < files.length; i++) {
    const file = files[i];
    const lastChunk = file.split("-").pop();
    if (lastChunk.length < 4 || lastChunk !== hash.slice(0, lastChunk.length)) {
      continue;
    }
    const stat = await promises.stat(rootDir + "/" + file);
    if (stat.isDirectory()) {
      matches.push(file);
    }
  }
  return matches;
}
async function downloadGitHubRepo(options) {
  const hash = await getGitHubRepoHash(options);
  const ifModifiedSince = options.ifModifiedSince;
  if (ifModifiedSince) {
    const expectedHash = typeof ifModifiedSince === "string" ? ifModifiedSince : ifModifiedSince.downloadType === "github" ? ifModifiedSince.hash : null;
    if (hash === expectedHash) {
      return "not_modified";
    }
  }
  options.target = options.target.replace("{hash}", hash);
  const rootDir = options.target = await prepareDirectoryForExport(options);
  const archiveTarget = rootDir + "/" + hash + ".zip";
  let exists = false;
  try {
    const stat = await promises.stat(archiveTarget);
    exists = stat.isFile();
  } catch (err) {
  }
  if (!exists) {
    const uri = `https://api.github.com/repos/${options.user}/${options.repo}/zipball/${hash}`;
    await downloadFile(
      {
        uri,
        headers: {
          Accept: "application/vnd.github.v3+json",
          Authorization: "token " + options.token
        }
      },
      archiveTarget
    );
  }
  const files = await promises.readdir(rootDir);
  const hashSearch = "-" + hash;
  for (let i = 0; i < files.length; i++) {
    const file = files[i];
    if (file === hash + ".zip") {
      continue;
    }
    const filename = rootDir + "/" + files[i];
    const stat = await promises.lstat(filename);
    const isDir = stat.isDirectory();
    if (
      // Remove symbolic links
      stat.isSymbolicLink() || // Remove if directory matches hash to avoid errors extracting zip
      isDir && filename.slice(0 - hashSearch.length) === hashSearch || // Remove if directory and cleanupOldDirectories is not disabled
      isDir && options.cleanupOldDirectories !== false || // Remove if file and cleanupOldFiles is enabled
      !isDir && options.cleanupOldFiles
    ) {
      try {
        await promises.rm(filename, {
          force: true,
          recursive: true
        });
      } catch (err) {
      }
    }
  }
  await unzip(archiveTarget, rootDir);
  const matchingDirs = await findMatchingDirs(rootDir, hash);
  if (matchingDirs.length !== 1) {
    throw new Error(`Error unpacking ${hash}.zip`);
  }
  const contentsDir = rootDir + "/" + matchingDirs[0];
  return {
    downloadType: "github",
    rootDir,
    contentsDir,
    hash
  };
}

export { downloadGitHubRepo };
