import chalk from "chalk";
import recursive from "recursive-readdir";
import fs from "fs";
import {
  ConfigFile,
  writePlaylistsAlphabet,
  writePlaylistsNew,
  writePlaylistsSearchTerms,
  simplifyName
} from ".";

/**
 * An OperationSet contains a function for each action the user can take,
 * nested nicely to represent possible paths. The first term passed on the
 * command line can be any of the top-level values; the second term passed can
 * be any values nested beneath that.
 */
export interface OperationSet {
  [key: string]: any;
  generate: () => void;
  list: {
    people: () => void;
    directories: () => void;
    tags: () => void;
    sources: () => void;
    ignores: () => void;
  };
  delete: {
    person: () => void;
    ignore: () => void;
    directory: () => void;
    tag: () => void;
    source: () => void;
  };
  ignore: () => void;
  add: {
    person: () => void;
    tag: () => void;
    source: () => void;
    directory: () => void;
  };
}

/**
 * Produces an OperationSet object that can be used to easily match the command
 * line input to the desired function. The first term passed to kodilist selects
 * a top-level key in the OperationSet. If the first term matched a function,
 * all further terms become its arguments; if the first term matched an object,
 * the second term selects a function and terms 3+ become its arguments.
 *
 * For example, `$ kodilist add source bbc.co.uk` matches
 * `['add']['source']['bbc.co.uk']()`.
 *
 * Arguments do not need to be directly passed to the matched function; the
 * full string is passed in when `Operations()` is invoked and each function
 * selects the portion it needs.
 */
export default function Operations(
  config: ConfigFile,
  paramSet: string[]
): OperationSet {
  const param = paramSet
    .slice(2)
    .join(" ")
    .toLowerCase();
  return {
    generate: async function() {
      const files: any[] = [];
      for (let dirName of config.directories) {
        const currentDirFiles = await recursive(dirName, config.ignoreList);
        currentDirFiles.forEach(filename =>
          files.push({
            filename,
            simpleName: simplifyName(filename),
            mtime: fs.statSync(filename).mtimeMs
          })
        );
      }
      files.sort((a, b) => (a.simpleName < b.simpleName ? -1 : 1));
      writePlaylistsSearchTerms(config, files);
      writePlaylistsNew(config, files);
      writePlaylistsAlphabet(config, files);
    },
    list: {
      people: () => config.searchTerms.people.forEach(s => console.log(s)),
      directories: () => config.directories.forEach(s => console.log(s)),
      tags: () => config.searchTerms.tags.forEach(s => console.log(s)),
      sources: () => config.searchTerms.sources.forEach(s => console.log(s)),
      ignores: () => config.ignoreList.forEach(s => console.log(s))
    },
    delete: {
      person: function() {
        const index = config.searchTerms.people.indexOf(param);
        if (index === -1) {
          console.log(`${chalk.red(param)} wasn't in list of people`);
          return;
        }
        config.searchTerms.people.splice(index, 1);
        config.save();
        console.log(`Stopped tracking ${chalk.green(param)}`);
      },
      directory: function() {
        const index = config.directories.indexOf(param);
        if (index === -1) {
          console.log(`${chalk.red(param)} wasn't in list of directories`);
          return;
        }
        config.directories.splice(index, 1);
        config.save();
        console.log(`Stopped tracking ${chalk.green(param)}`);
      },
      tag: function() {
        const index = config.searchTerms.tags.indexOf(param);
        if (index === -1) {
          console.log(`${chalk.red(param)} wasn't in list of tags`);
          return;
        }
        config.searchTerms.tags.splice(index, 1);
        config.save();
        console.log(`Stopped tracking ${chalk.green(param)}`);
      },
      source: function() {
        const index = config.searchTerms.sources.indexOf(param);
        if (index === -1) {
          console.log(`${chalk.red(param)} wasn't in list of sources`);
          return;
        }
        config.searchTerms.sources.splice(index, 1);
        config.save();
        console.log(`Stopped tracking ${chalk.green(param)}`);
      },
      ignore: function() {
        const index = config.ignoreList.indexOf(param);
        if (index === -1) {
          console.log(`${chalk.red(param)} wasn't on ignore list`);
          return;
        }
        config.ignoreList.splice(index, 1);
        config.save();
        console.log(`Stopped ignoring ${chalk.green(param)}`);
      }
    },
    ignore: function() {
      const toIgnore = paramSet.slice(1).join(" ");
      if (config.ignoreList.includes(toIgnore)) {
        console.log(`Already ignoring ${chalk.blue(toIgnore)}`);
      } else {
        config.ignoreList.push(toIgnore);
        console.log(`Now ignoring ${chalk.green(toIgnore)}`);
      }
      config.save();
    },
    add: {
      person: function(): void {
        if (config.searchTerms.people.includes(param)) {
          console.log("Already tracking", chalk.blue(param));
        } else {
          config.searchTerms.people.push(param);
          config.save();
          console.log("Now tracking", chalk.blue(param));
        }
      },
      tag: function(): void {
        if (config.searchTerms.tags.includes(param)) {
          console.log("Already tracking", chalk.blue(param));
        } else {
          config.searchTerms.tags.push(param);
          config.save();
          console.log("Now tracking", chalk.blue(param));
        }
      },
      source: function(): void {
        if (config.searchTerms.sources.includes(param)) {
          console.log("Already tracking", chalk.blue(param));
        } else {
          config.searchTerms.sources.push(param);
          config.save();
          console.log("Now tracking", chalk.blue(param));
        }
      },
      directory: function(): void {
        if (config.directories.includes(paramSet[2])) {
          console.log("Already tracking", chalk.blue(paramSet[2]));
        } else {
          config.directories.push(paramSet[2]);
          config.save();
          console.log("Now tracking", chalk.blue(paramSet[2]));
        }
      }
    }
  };
}
