import { ExecutorContext } from '@nrwl/devkit';
import { join } from 'path';
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
import { ChildProcess, spawn } from 'child_process';
import { ReactNativeBuildOptions } from './schema';
import { chmodAndroidGradlewFiles } from '../../utils/chmod-android-gradle-files';
export interface ReactNativeBuildOutput {
  success: boolean;
}

let childProcess: ChildProcess;

export default async function* buildAndroidExecutor(
  options: ReactNativeBuildOptions,
  context: ExecutorContext
): AsyncGenerator<ReactNativeBuildOutput> {
  const projectRoot =
    context.projectsConfigurations.projects[context.projectName].root;
  ensureNodeModulesSymlink(context.root, projectRoot);
  chmodAndroidGradlewFiles(join(projectRoot, 'android'));

  try {
    await runCliBuild(context.root, projectRoot, options);
    yield { success: true };
  } finally {
    if (childProcess) {
      childProcess.kill();
    }
  }
}

function runCliBuild(
  workspaceRoot: string,
  projectRoot: string,
  options: ReactNativeBuildOptions
) {
  return new Promise((resolve, reject) => {
    const gradleCommand = getGradleCommand(options);

    childProcess = spawn(
      process.platform === 'win32' ? 'gradlew.bat' : './gradlew',
      [gradleCommand],
      {
        cwd: join(workspaceRoot, projectRoot, 'android'),
        stdio: [0, 1, 2],
      }
    );

    // Ensure the child process is killed when the parent exits
    process.on('exit', () => childProcess.kill());
    process.on('SIGTERM', () => childProcess.kill());

    childProcess.on('error', (err) => {
      reject(err);
    });
    childProcess.on('exit', (code) => {
      if (code === 0) {
        resolve(code);
      } else {
        reject(code);
      }
    });
  });
}

function getGradleCommand(options: ReactNativeBuildOptions) {
  if (options?.gradleTask) {
    return options.gradleTask;
  }
  if (options.apk) {
    if (options.debug) {
      return 'assembleDebug';
    }
    return 'assembleRelease';
  } else {
    if (options.debug) {
      return 'bundleDebug';
    }
    return 'bundleRelease';
  }
}
