import { CliTerseError } from '@alwaysai/alwayscli';
import { existsSync } from 'fs';
import { ALWAYSAI_CLI_EXECUTABLE_NAME } from '../../constants';
import { checkUserIsLoggedInComponent } from '../user';
import { JsSpawner } from '../../util';
import { appCheckComponent } from './app-check-component';
import { AppJsonFile, TargetJsonFile } from '../../core/app';
import { ALWAYSAI_OS_PLATFORM } from '../../environment';
import {
  APP_JSON_FILE_NAME,
  VENV_BIN_ACTIVATE,
  VENV_SCRIPTS_ACTIVATE
} from '../../paths';

const BASH_INITIAL_ARGS = ['-o', 'onecmd', '-O', 'huponexit', '-c'];

export async function appStartComponent(
  props: {
    noSuperuser?: boolean;
    volumes?: string[];
    env_vars?: string[];
    args?: string[];
  } = {}
) {
  const { noSuperuser, volumes, env_vars, args = [] } = props;
  await checkUserIsLoggedInComponent({ yes: true });
  await appCheckComponent();

  const appJson = AppJsonFile().read();
  const startScript = appJson.scripts?.start;
  if (!startScript) {
    throw new CliTerseError(
      `This application does not define a "start" script in its application configuration file "${APP_JSON_FILE_NAME}"`
    );
  }

  const targetJsonFile = TargetJsonFile();
  const targetJson = targetJsonFile.read();
  let exitCode: number | void;
  const quotedArgsString = args.map((arg) => `'${arg}'`).join(' ');
  const tty = process.stdin.isTTY;

  switch (targetJson.targetProtocol) {
    case 'docker:': {
      const spawner = targetJsonFile.readContainerSpawner({
        volumes,
        env_vars
      });
      exitCode = await spawner.runForeground({
        exe: '/bin/bash',
        args: [
          ...BASH_INITIAL_ARGS,
          `. ${VENV_BIN_ACTIVATE} && ${startScript} ${quotedArgsString}`
        ],
        cwd: '.',
        tty,
        expose5000: true,
        superuser: !noSuperuser
      });
      break;
    }

    // This case differs from "docker:"" only in the extra single quotes around the command
    case 'ssh+docker:': {
      const spawner = targetJsonFile.readContainerSpawner({
        volumes,
        env_vars
      });
      exitCode = await spawner.runForeground({
        exe: '/bin/bash',
        args: [
          ...BASH_INITIAL_ARGS,
          `'. ${VENV_BIN_ACTIVATE} && ${startScript} ${quotedArgsString}'`
        ],
        cwd: '.',
        tty,
        expose5000: true,
        superuser: !noSuperuser
      });
      break;
    }

    case 'native:': {
      switch (ALWAYSAI_OS_PLATFORM) {
        case 'win32': {
          if (!existsSync(VENV_SCRIPTS_ACTIVATE)) {
            throw new CliTerseError(
              `File not found "${VENV_SCRIPTS_ACTIVATE}". Did you run "${ALWAYSAI_CLI_EXECUTABLE_NAME} app install"?`
            );
          }
          exitCode = await JsSpawner().runForeground({
            exe: 'cmd.exe',
            args: [
              '/c',
              `${VENV_SCRIPTS_ACTIVATE} && ${startScript} ${args.join(' ')}`
            ]
          });
          break;
        }

        case 'darwin': {
          if (!existsSync(VENV_BIN_ACTIVATE)) {
            throw new CliTerseError(
              `File not found "${VENV_BIN_ACTIVATE}". Did you run "${ALWAYSAI_CLI_EXECUTABLE_NAME} app install"?`
            );
          }
          exitCode = await JsSpawner().runForeground({
            exe: '/bin/sh',
            args: [
              `-c`,
              `. ${VENV_BIN_ACTIVATE} && ${startScript} ${quotedArgsString}`
            ]
          });
          break;
        }

        default: {
          throw new CliTerseError(
            `OS ${ALWAYSAI_OS_PLATFORM} not supported for native start`
          );
        }
      }
      break;
    }

    default: {
      throw new Error('Unsupported protocol');
    }
  }

  return exitCode || 0;
}
