import fg from "fast-glob";
import fs from "fs";
import assert from "assert";
import { log } from "./logging.js";
import Path from "path";
import { execSync } from "child_process";

/*
 Fixes some common problems with fast-glob on windows.
 */
export const glob = (path) => {
  if(process.platform === "win32") {
    // fast-glob doesn't understand \\ or C:\
    const fixed_path = path.replace("C:","").replaceAll('\\', '/')

    return fg.sync(fixed_path);
  } else {
    return fg.sync(path);
  }
}

export const changed = (source, target) => {
  // we want the source to error if it doesn't exist
  const s_stat = fs.statSync(source);
  // but target might not exist, which would mean it's a change
  const t_stat = fs.statSync(target, {throwIfNoEntry: false});

  return !t_stat || s_stat.mtimeMs > t_stat.mtimeMs;
}

/* Makes a directory recursively. */
export const mkdir = (dir) => {
  if(!fs.existsSync(dir)) {
    log.debug(`making dir ${dir}`);
    fs.mkdirSync(dir, { recursive: true });
  }
}

/*
 Given any path to a file, makes sure all its directories are in place.

 Example:

 ```javascript
 console.log("PRINT");
 ```

 - `param string` -- path to file
 */
export const mkdir_to = (target) => {
  let dirs = Path.parse(target);
  mkdir(dirs.dir);
}

/* Convenience function that makes sure the dir is there then writes the data. */
export const write = (target, data) => {
  mkdir_to(target);
  fs.writeFileSync(target, data);
}

/*
 Like write but does an OS copy after making the target dir. When given a
 filter it will give the filter function the arguments, let it return contents,
 then write those instead of a copy.
 */
export const copy = (src, dest, filter=null) => {
  mkdir_to(dest);

  if(filter) {
    try {
      let raw_data = fs.readFileSync(src);
      let data = filter(src, dest, raw_data);
      write(dest, data);
    } catch (error) {
      log.error(error, "Problem with filter write in copy");
    }
  } else {
    fs.copyFileSync(src, dest);
  }
}

export const rm = (file_name, ignore=false) => {
  log.warn("Deleting file", file_name);
  try {
    fs.unlinkSync(file_name);
  } catch(error) {
    if(ignore) {
      log.debug(`Requested delete file ${file_name} doesn't exist, but ignored.`);
    } else {
      log.error(error, file_name);
      throw error;
    }
  }
}

export const index_rollup = (src_index, target_index, contents, key) => {
  let index = JSON.parse(fs.readFileSync(src_index));

  index[key] = glob(contents).map(f => JSON.parse(fs.readFileSync(f)));
  assert(index[key] !== undefined, `Attempt to load contents into ${key} for ${src_index} failed with undefined.`);

  write(target_index, JSON.stringify(index));

  return index;
}

export const exec = (cmd, opts) => {
  log.info(`EXEC ${cmd}`);
  return execSync(cmd, opts);
}

export const exec_i = (cmd, opts={}) => {
  return exec(cmd, {stdio: [process.stdout, process.stderr, process.stdin], ...opts});
}


export default {
  index_rollup, rm, exec, changed, mkdir, mkdir_to, write, copy, exec_i, glob
}
