import * as cp from 'child_process';
import { promisify } from 'util';
import * as fs from 'fs';
import * as path from 'path';

const rawExecFile = cp.execFile as any;

// ─── 类型重导出 ───
export type { ChildProcess, SpawnOptions, ExecOptions, ExecFileOptions } from 'child_process';
export type { ChildProcessWithoutNullStreams } from 'child_process';

// ─── spawn ───
export const spawn = ((
  command: string,
  argsOrOptions?: readonly string[] | cp.SpawnOptions,
  maybeOptions?: cp.SpawnOptions,
): cp.ChildProcess => {
  if (Array.isArray(argsOrOptions)) {
    return cp.spawn(command, argsOrOptions, { ...maybeOptions, windowsHide: true });
  }
  if (argsOrOptions && typeof argsOrOptions === 'object') {
    return cp.spawn(command, { ...(argsOrOptions as cp.SpawnOptions), windowsHide: true });
  }
  return cp.spawn(command, { windowsHide: true });
}) as typeof cp.spawn;

// ─── exec ───
export const exec = ((
  command: string,
  optionsOrCallback?: cp.ExecOptions | ((error: cp.ExecException | null, stdout: string, stderr: string) => void),
  maybeCallback?: (error: cp.ExecException | null, stdout: string, stderr: string) => void,
): cp.ChildProcess => {
  if (typeof optionsOrCallback === 'function') {
    return cp.exec(command, { windowsHide: true }, optionsOrCallback);
  }
  if (optionsOrCallback) {
    return cp.exec(command, { ...optionsOrCallback, windowsHide: true }, maybeCallback!);
  }
  return cp.exec(command, { windowsHide: true });
}) as typeof cp.exec;

// ─── execAsync ───
export const execAsync = promisify(exec);

// ─── execFile ───
export const execFile = ((
  file: string,
  argsOrOptions?: readonly string[] | cp.ExecFileOptions | null,
  optionsOrCallback?: cp.ExecFileOptions | ((error: cp.ExecException | null, stdout: string, stderr: string) => void) | null,
  maybeCallback?: ((error: cp.ExecException | null, stdout: string, stderr: string) => void) | null,
): cp.ChildProcess => {
  if (Array.isArray(argsOrOptions)) {
    if (typeof optionsOrCallback === 'function') {
      return rawExecFile(file, argsOrOptions, { windowsHide: true }, optionsOrCallback);
    }
    if (optionsOrCallback && typeof optionsOrCallback === 'object') {
      return rawExecFile(file, argsOrOptions, { ...optionsOrCallback, windowsHide: true }, maybeCallback!);
    }
    return rawExecFile(file, argsOrOptions, { windowsHide: true });
  }
  if (typeof argsOrOptions === 'function') {
    return rawExecFile(file, { windowsHide: true }, argsOrOptions as any);
  }
  if (argsOrOptions && typeof argsOrOptions === 'object') {
    if (typeof optionsOrCallback === 'function') {
      return rawExecFile(file, { ...(argsOrOptions as cp.ExecFileOptions), windowsHide: true }, optionsOrCallback as any);
    }
    return rawExecFile(file, { ...(argsOrOptions as cp.ExecFileOptions), windowsHide: true });
  }
  return rawExecFile(file, { windowsHide: true });
}) as typeof cp.execFile;

// ─── Shell 配置 ───
export function resolveShellConfig(): { shell: string; args: string[] } {
  if (process.platform === 'win32') {
    const gitBash = findGitBash();
    if (gitBash) {
      return { shell: gitBash, args: ['-lc'] };
    }
    return {
      shell: process.env.ComSpec || 'cmd.exe',
      args: ['/d', '/s', '/c'],
    };
  }
  return {
    shell: process.env.SHELL || '/bin/sh',
    args: ['-lc'],
  };
}

function findGitBash(): string | null {
  const candidates = [
    process.env.ProgramFiles ? path.join(process.env.ProgramFiles, 'Git', 'bin', 'bash.exe') : null,
    process.env['ProgramFiles(x86)'] ? path.join(process.env['ProgramFiles(x86)']!, 'Git', 'bin', 'bash.exe') : null,
    ...('CDEFGH'.split('').flatMap(d => [
      path.join(`${d}:\\`, 'Git', 'bin', 'bash.exe'),
      path.join(`${d}:\\`, 'Program Files', 'Git', 'bin', 'bash.exe'),
    ])),
  ].filter((item): item is string => Boolean(item));

  for (const candidate of candidates) {
    if (fs.existsSync(candidate)) {
      return candidate;
    }
  }
  return null;
}
