import chalk from 'chalk';
import { watchFile } from 'fs';
import path from 'path';
import resolveFrom from 'resolve-from';

import * as Log from '../log';
import { memoize } from './fn';

const debug = require('debug')('expo:utils:fileNotifier') as typeof console.log;

/** Observes and reports file changes. */
export class FileNotifier {
  constructor(
    /** Project root to resolve the module IDs relative to. */
    private projectRoot: string,
    /** List of module IDs sorted by priority. Only the first file that exists will be observed. */
    private moduleIds: string[],
    private settings: {
      /** An additional warning message to add to the notice. */
      additionalWarning?: string;
    } = {}
  ) {}

  /** Get the file in the project. */
  private resolveFilePath(): string | null {
    for (const moduleId of this.moduleIds) {
      const filePath = resolveFrom.silent(this.projectRoot, moduleId);
      if (filePath) {
        return filePath;
      }
    }
    return null;
  }

  public startObserving() {
    const configPath = this.resolveFilePath();
    if (configPath) {
      debug(`Observing ${configPath}`);
      return this.watchFile(configPath);
    }
    return configPath;
  }

  /** Watch the file and warn to reload the CLI if it changes. */
  public watchFile = memoize(this.startWatchingFile.bind(this));

  private startWatchingFile(filePath: string): string {
    const configName = path.relative(this.projectRoot, filePath);
    watchFile(filePath, (cur: any, prev: any) => {
      if (prev.size || cur.size) {
        Log.log(
          `\u203A Detected a change in ${chalk.bold(
            configName
          )}. Restart the server to see the new results.` + (this.settings.additionalWarning || '')
        );
      }
    });
    return filePath;
  }
}
