import { BaseAdapter } from '../base.adapter';
import { PROVIDER } from '../../utils/provider';
import { prompt } from 'enquirer';
import { Rule, applyTemplates } from '@angular-devkit/schematics';
import { QUESTIONS } from '../../utils/questions';
import { NxDeployItInitSchematicSchema } from '../../schematics/init/schema';
import { getDistributionPath, getProjectConfig } from '../../utils/workspace';
import { NxDeployItDeployBuilderSchema } from '../../builders/deploy/schema';
import { BuilderOutput, BuilderContext } from '@angular-devkit/architect';
import { Observable, from } from 'rxjs';
import { switchMap, map } from 'rxjs/operators';
import { resolve } from 'path';
import * as ncc from '@vercel/ncc';
import { ensureDirSync, ensureFileSync } from 'fs-extra';
import { writeFileSync } from 'fs';

export class NestJSAdapter extends BaseAdapter {
  async extendOptionsByUserInput() {
    await super.extendOptionsByUserInput();
    const options = this.options as NxDeployItInitSchematicSchema;
    const questions: any[] = [];

    if (
      options.provider === PROVIDER.GOOGLE_CLOUD_PLATFORM &&
      !options['gcp:region']
    ) {
      questions.push(QUESTIONS.gcpRegionCloudFunctions);
    }

    const anwsers = await prompt(questions);
    this.options = {
      ...options,
      ...anwsers
    };
  }

  addRequiredDependencies() {
    const dependencies = super.addRequiredDependencies();

    if (this.options.provider === PROVIDER.AZURE) {
      dependencies.push(
        {
          name: '@nestjs/azure-func-http',
          version: '^0.4.2'
        },
        {
          name: '@azure/functions',
          version: '^1.2.0'
        }
      );
    }
    if (this.options.provider === PROVIDER.AWS) {
      dependencies.push({
        name: 'aws-serverless-express',
        version: '^3.3.6'
      });
    }
    return dependencies;
  }

  getApplicationTypeTemplate(): Rule {
    return applyTemplates({
      rootDir: 'src',
      getRootDirectory: () => 'src',
      stripTsExtension: (s: string) => s.replace(/\.ts$/, ''),
      getRootModuleName: () => 'AppModule',
      getRootModulePath: () => 'app/app.module',
      projectName: this.options.project
    });
  }

  getApplicationTemplatePath() {
    return `${super.getApplicationTemplatePath()}/nestjs/`;
  }

  getDeployActionConfiguration(): any {
    const config = super.getDeployActionConfiguration();

    // TODO: use in deploy & destroy via angular.json config
    // if (options.provider === PROVIDER.GOOGLE_CLOUD_PLATFORM && options.region) {
    //   args.push('-c', `gcp:region=${options.region}`);
    // }

    return config;
  }

  getDestroyActionConfiguration(): any {
    const config = super.getDestroyActionConfiguration();
    return config;
  }

  deploy(
    context: BuilderContext,
    cwd: string,
    options: NxDeployItDeployBuilderSchema,
    configuration: string,
    targetOptions: any
  ): Observable<BuilderOutput> {
    const distributationPath = getDistributionPath(context);

    const project = getProjectConfig(context);
    const infrastructureFolder = resolve(
      context.workspaceRoot,
      project.root,
      'infrastructure'
    );

    const processCwd = process.cwd();
    process.chdir(infrastructureFolder);

    const build$: Observable<BuilderOutput> = from(
      ncc(resolve(infrastructureFolder, 'functions/main/index.ts'), {
        cache: resolve(infrastructureFolder, 'buildcache')
      })
    ).pipe(
      map(
        (buildResult: {
          code: string;
          asset: { [index: string]: { source: string } };
        }) => {
          process.chdir(processCwd);
          ensureDirSync(resolve(infrastructureFolder, 'functions/dist/main'));
          // compiled javascript
          writeFileSync(
            resolve(infrastructureFolder, 'functions/dist/main/index.js'),
            buildResult.code
          );
          // assets
          for (const file in buildResult.asset) {
            const content = buildResult.asset[file];
            ensureFileSync(
              resolve(infrastructureFolder, `functions/dist/main/${file}`)
            );
            writeFileSync(
              resolve(infrastructureFolder, `functions/dist/main/${file}`),
              content.source.toString()
            );
          }
          return { success: true };
        }
      )
    );

    return build$.pipe(
      switchMap(() =>
        this.up(
          cwd,
          options,
          configuration,
          targetOptions,
          distributationPath,
          context.target.project
        )
      )
    );
  }
}