/**
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import { AbsolutePath } from '../core';
import { spawn } from 'node:child_process';

export type CliOptions = {
  args: string[];
  cwd?: AbsolutePath;
  onArgsStringified?: (commandWithArgs: string) => void;
  onStdout?: (data: string) => void;
  onStderr?: (data: string) => void;
  shell?: boolean;
};

export abstract class CliExecutor {
  abstract run(command: string, options?: CliOptions): Promise<string>;
}

export class RealCliExecutor extends CliExecutor {
  async run(command: string, options?: CliOptions): Promise<string> {
    return new Promise((resolve, reject) => {
      let commandWithArgs = command;
      let argStrings: string[] = [];
      if (options?.args) {
        argStrings = options.args;
        commandWithArgs += ' ' + argStrings.join(' ');
      }
      options?.onArgsStringified?.(commandWithArgs);
      const process = spawn(command, argStrings, {
        cwd: options?.cwd?.toString(),
        shell: options?.shell ? true : false,
      });

      let stdout = '';
      let stderr = '';

      process.stdout.on('data', (data) => {
        const output = data.toString();
        stdout += output;
        options?.onStdout?.(output);
      });

      process.stderr.on('data', (data) => {
        const output = data.toString();
        stderr += output;
        options?.onStderr?.(output);
      });

      process.on('error', (error) => {
        reject(error);
      });

      process.on('close', (code) => {
        if (code === 0) {
          resolve(stdout);
        } else {
          reject(
            new Error(`Command failed with code ${code}\nStderr: ${stderr}`)
          );
        }
      });
    });
  }
}