/* eslint-disable no-console */
import { spawn } from 'child_process';
import { platform, exit, stdout, stderr, stdin, on } from 'process';

import { terminateProcess } from '@/utils/node/process';
import { pathExists, readJsonFile } from '@/utils/node/fs';
import { getConfigDir } from '@/utils/node/path';

async function getCurrentKoboldBinary() {
  try {
    const configPath = getConfigDir();
    if (!(await pathExists(configPath))) {
      return null;
    }

    const config = await readJsonFile<{ currentKoboldBinary?: string }>(
      configPath
    );
    return config?.currentKoboldBinary || null;
  } catch {
    return null;
  }
}

export async function handleCliMode(args: string[]) {
  const currentBinary = await getCurrentKoboldBinary();

  if (!currentBinary) {
    console.error(
      'Error: No binary found. Please run the GUI first to download the binary.'
    );
    exit(1);
  }

  if (!(await pathExists(currentBinary))) {
    console.error(`Error: Binary not found at: ${currentBinary}`);
    console.error('Please run the GUI to download and configure the binary.');
    exit(1);
  }

  return new Promise<void>((resolve, reject) => {
    const isWindows = platform === 'win32';

    const child = spawn(currentBinary, args, {
      stdio: isWindows ? 'pipe' : 'inherit',
      detached: false,
    });

    if (isWindows) {
      child.stdout?.setEncoding('utf8');
      child.stderr?.setEncoding('utf8');

      child.stdout?.on('data', (data) => {
        stdout.write(data.toString());
      });

      child.stderr?.on('data', (data) => {
        stderr.write(data.toString());
      });

      if (child.stdin && stdin.readable) {
        stdin.pipe(child.stdin);
      }
    }

    child.on('exit', (code, signal) => {
      if (signal) {
        console.log(`\nProcess terminated with signal: ${signal}`);
        exit(128 + (signal === 'SIGTERM' ? 15 : 2));
      } else if (code !== null) {
        exit(code);
      } else {
        resolve();
      }
    });

    child.on('error', (error) => {
      console.error(`Failed to start: ${error.message}`);
      reject(error);
    });

    const handleSignal = async () => {
      console.log('\nReceived termination signal, terminating...');
      if (!child.killed) {
        await terminateProcess(child);
      }
    };

    on('SIGINT', handleSignal);
    on('SIGTERM', handleSignal);
    if (platform === 'win32') {
      on('SIGBREAK', handleSignal);
    }
  });
}
