import { fsPath, glob } from '../common';
import { IManifest, IManifestResource } from './types';
import * as util from './util';

/**
 * Represents the manifest of a folder on the file-system.
 */
export class Manifest {
  public readonly path: string;
  private readonly rootDir: string;
  private readonly urlPrefix: string | undefined;

  constructor(args: {
    rootDir: string;
    path: string; // Glob pattern.
    urlPrefix?: string;
  }) {
    this.rootDir = args.rootDir;
    this.path = `/${util.cleanPath(args.path)}`;
    this.urlPrefix = args.urlPrefix
      ? `/${util.cleanPath(args.urlPrefix)}`
      : undefined;
  }

  public get dir() {
    return fsPath.join(this.rootDir, this.path);
  }

  public async getPaths() {
    const paths = await glob.find(this.dir);
    return paths;
  }

  public async toObject(args: { loadExtensions?: string[] } = {}) {
    try {
      const { loadExtensions } = args;
      const paths = await this.getPaths();
      const wait = paths
        .filter(filePath => filePath !== this.rootDir)
        .filter(filePath => !filePath.includes('@2x.')) // NB: The 2x image is derived from the 1x version.
        .map(async filePath =>
          util.toResource({
            filePath,
            rootPath: this.rootDir,
            urlPrefix: this.urlPrefix,
            loadExtensions,
          }),
        );
      const files = await Promise.all(wait);
      const result: IManifest = {
        path: this.path,
        resources: files.filter(f => Boolean(f)) as IManifestResource[],
      };
      return result;
    } catch (error) {
      throw new Error(
        `Failed to convert manifest '${this.path}' into an object.`,
      );
    }
  }
}
