/*
 * notion-enhancer
 * (c) 2020 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
 * (https://dragonwocky.me/notion-enhancer) under the MIT license
 */

'use strict';

const os = require('os'),
  path = require('path'),
  fs = require('fs-extra'),
  { execSync } = require('child_process');

// used to differentiate between "enhancer failed" and "code broken" errors.
class EnhancerError extends Error {
  constructor(message) {
    super(message);
    this.name = 'EnhancerError';
  }
}

// checks if being run on the windows subsystem for linux:
// used to modify windows notion app.
const is_wsl =
    process.platform === 'linux' &&
    os.release().toLowerCase().includes('microsoft'),
  // ~/.notion-enhancer absolute path.
  __data = path.resolve(
    `${
      is_wsl
        ? (() => {
            const stdout = execSync('cmd.exe /c echo %systemdrive%%homepath%', {
                encoding: 'utf8',
              }),
              drive = stdout[0];
            return `/mnt/${drive.toLowerCase()}${stdout
              .replace(/\\/g, '/')
              .slice(2)
              .trim()}`;
          })()
        : os.homedir()
    }/.notion-enhancer`
  );

// transform a wsl filepath to its relative windows filepath if necessary.
function realpath(hack_path) {
  if (!is_wsl) return hack_path.replace(/\\/g, '/');
  hack_path = fs.realpathSync(hack_path);
  if (hack_path.startsWith('/mnt/')) {
    hack_path = `${hack_path[5].toUpperCase()}:${hack_path.slice(6)}`;
  } else hack_path = `//wsl$/${process.env.WSL_DISTRO_NAME}${hack_path}`;
  return hack_path;
}

// gets possible system notion app filepaths.
function getNotionResources() {
  let folder = '';
  switch (process.platform) {
    case 'darwin':
      folder = '/Applications/Notion.app/Contents/Resources';
      break;
    case 'win32':
      folder = process.env.LOCALAPPDATA + '\\Programs\\Notion\\resources';
      break;
    case 'linux':
      if (is_wsl) {
        const stdout = execSync('cmd.exe /c echo %localappdata%', {
            encoding: 'utf8',
          }),
          drive = stdout[0];
        folder = `/mnt/${drive.toLowerCase()}${stdout
          .replace(/\\/g, '/')
          .slice(2)
          .trim()}/Programs/Notion/resources`;
      } else {
        for (let loc of [
          '/usr/lib/notion-desktop/resources', // https://github.com/davidbailey00/notion-deb-builder/
          '/opt/notion-app', // https://aur.archlinux.org/packages/notion-app/
          '/opt/notion', // https://github.com/jaredallard/notion-app
        ]) {
          if (fs.pathExistsSync(loc)) folder = loc;
        }
      }
  }
  if (!folder)
    throw new EnhancerError('nothing found: platform not supported.');
  return folder;
}

// lists/fetches all available extensions + themes
function getEnhancements() {
  const modules = {
    loaded: [],
    invalid: [],
    dirs: fs
      .readdirSync(path.resolve(`${__dirname}/../mods`))
      .filter((dir) => !dir.startsWith('.')),
    IDs: [],
  };
  for (let dir of modules.dirs) {
    try {
      const mod = require(`../mods/${dir}/mod.js`);
      if (!mod.tags) mod.tags = [];
      if (
        !mod.id ||
        modules.IDs.includes(mod.id) ||
        !mod.name ||
        !mod.version ||
        !mod.author ||
        !mod.tags.every((tag) => typeof tag === 'string') ||
        (mod.fonts && !mod.fonts.every((font) => typeof font === 'string')) ||
        (mod.options &&
          !mod.options.every((opt) =>
            ['toggle', 'select', 'input', 'file', 'color'].includes(opt.type)
          ))
      )
        throw Error;
      mod.defaults = {};
      for (let opt of mod.options || []) {
        if (
          Object.keys(opt.platformOverwrite || {}).some(
            (platform) => process.platform === platform
          )
        ) {
          mod.defaults[opt.key] = opt.platformOverwrite[process.platform];
        } else
          mod.defaults[opt.key] = Array.isArray(opt.value)
            ? opt.value[0]
            : opt.value;
      }
      modules.IDs.push(mod.id);
      modules.loaded.push({
        ...mod,
        dir,
      });
      if (!mod.tags.includes('core')) mod.alwaysActive = false;
    } catch (err) {
      // console.error(err);
      modules.invalid.push(dir);
    }
  }
  modules.loaded = modules.loaded.sort((a, b) => a.name.localeCompare(b.name));
  const priority = require('./store.js')('mods', { priority: [] }).priority;
  modules.loaded = [
    ...modules.loaded.filter((m) => m.tags.includes('core')),
    ...modules.loaded.filter(
      (m) => !m.tags.includes('core') && !priority.includes(m.id)
    ),
    ...priority
      .map((id) => modules.loaded.find((m) => m.id === id))
      .filter((m) => m),
  ];
  return modules;
}

// attempts to read a JSON file, falls back to empty object.
function getJSON(from) {
  try {
    return fs.readJsonSync(from);
  } catch (err) {
    return {};
  }
}

// wait for console input, returns keys when enter pressed.
function readline() {
  return new Promise((res, rej) => {
    process.stdin.resume();
    process.stdin.setEncoding('utf8');
    process.stdin.on('data', (key) => {
      if (key === '\u0003') process.exit(); // CTRL+C
      process.stdin.pause();
      res(key.trim());
    });
  });
}

// construct a HTMLElement from a string
function createElement(html) {
  const template = document.createElement('template');
  template.innerHTML = html.trim();
  return template.content.firstElementChild;
}

module.exports = {
  EnhancerError,
  is_wsl,
  __data,
  realpath,
  getNotionResources,
  getEnhancements,
  getJSON,
  readline,
  createElement,
};