const { detectProject } = require('./provider-detector');
const { runCommand, runInteractiveCommand } = require('./command-runner');
const { DdevProvider } = require('./ddev-provider');
const { LandoProvider } = require('./lando-provider');

class UnsupportedProjectError extends Error {
  constructor(project) {
    const provider = project ? project.provider : 'unknown';
    super(`Env Menu does not support ${provider} projects from the CLI yet.`);
    this.code = 'UNSUPPORTED_PROVIDER';
    this.provider = provider;
  }
}

class ProjectNotFoundError extends Error {
  constructor(startDir) {
    super(`No supported project was found at or above ${startDir}.`);
    this.code = 'PROJECT_NOT_FOUND';
  }
}

class DatabaseNotAvailableError extends Error {
  constructor(project) {
    super(`No database is available for ${project.name}.`);
    this.code = 'DATABASE_NOT_AVAILABLE';
  }
}

class EnvironmentController {
  constructor({ detector = detectProject, providers } = {}) {
    this.detector = detector;
    this.providers = providers || {
      ddev: new DdevProvider({ runCommand, runInteractiveCommand }),
      lando: new LandoProvider({ runCommand, runInteractiveCommand })
    };
  }

  resolveProject(startDir = process.cwd()) {
    const project = this.detector(startDir);
    if (!project) {
      throw new ProjectNotFoundError(startDir);
    }
    return project;
  }

  providerFor(project) {
    const provider = this.providers[project.provider];
    if (!provider) {
      throw new UnsupportedProjectError(project);
    }
    return provider;
  }

  async start(startDir = process.cwd(), options = {}) {
    const project = this.resolveProject(startDir);
    const runtime = await this.providerFor(project).start(project, options);
    return { project, runtime };
  }

  async stop(startDir = process.cwd(), options = {}) {
    const project = this.resolveProject(startDir);
    const runtime = await this.providerFor(project).stop(project, options);
    return { project, runtime };
  }

  async status(startDir = process.cwd()) {
    const project = this.resolveProject(startDir);
    const runtime = await this.providerFor(project).status(project);
    return { project, runtime };
  }

  async db(startDir = process.cwd()) {
    const project = this.resolveProject(startDir);
    const provider = this.providerFor(project);

    if (!provider.databaseUrl) {
      throw new DatabaseNotAvailableError(project);
    }

    const url = await provider.databaseUrl(project);
    return { project, url };
  }
}

module.exports = {
  DatabaseNotAvailableError,
  EnvironmentController,
  ProjectNotFoundError,
  UnsupportedProjectError
};
