import { constants, fs, fsPath, log, express } from './common';

/**
 * Express middleware for serving the editor resources.
 */
export const editorServer = () => {
  log.info.gray(`Initializing ${log.magenta('editor.code')} routes:\n`);
  const router = express.router();
  const cache: { [url: string]: { file?: string } } = {};
  const BASE_URL = constants.URL.BASE;

  const route = (url: string, dir: string) => {
    log.info.gray(` - route: ${log.cyan(url)}`);
    log.info.gray(` - dir:   ${log.cyan(dir)}`);
    log.info();

    const toPath = (url: string) => {
      if (url.startsWith(BASE_URL)) {
        return url.substr(BASE_URL.length + 1);
      }

      const index = url.indexOf('/vs/');
      if (index > -1) {
        return index > -1 ? url.substr(index + 4) : undefined;
      }

      return undefined;
    };

    /**
     * GET: Return the monaco editor dynamically loaded resources.
     */
    router.get(url, async (req, res) => {
      const done = (file?: string) => {
        cache[req.url] = { file };
        return file ? res.sendFile(file) : res.status(404).send('Not found.');
      };

      // Derive the path.
      const path = toPath(req.url);

      // Check cache.
      const cached = cache[req.url];
      if (cached) {
        return done(cached.file);
      }

      // Don't allow any parent system files to be served.
      if (!path || path.indexOf('..') > -1) {
        return done(undefined);
      }

      // Ensure the file exists.
      const file = fsPath.join(dir, path);
      const exists = await fs.pathExists(file);
      if (!exists) {
        return done(undefined);
      }

      // Send the "Monaco Editor" resource.
      if (constants.IS_DEV) {
        log.info(log.magenta('GET'), log.cyan(req.url));
        log.info.gray(`    ${file}`);
        log.info();
      }
      return done(file);
    });
  };

  route(`${BASE_URL}/*`, constants.EDITOR_RESOURCES.LOCAL);
  route(`/vs/*`, constants.VS.SOURCE);
  route(`*/vs/*`, constants.VS.SOURCE);

  return router;
};
