import { existsSync, writeFile, stat, readFile } from 'fs';
import { execFile } from 'child_process';
import { promisify } from 'util';
import ora = require('ora');

import {
  PRIVATE_KEY_FILE_PRETTY_PATH,
  PRIVATE_KEY_FILE_PATH,
  PUBLIC_KEY_FILE_PATH,
} from '../constants';
import { TerseError } from '@alwaysai/alwayscli';

export async function writePrivateKeyFileComponent() {
  const spinner = ora(`Write private key file "${PRIVATE_KEY_FILE_PRETTY_PATH}"`).start();
  if (!existsSync(PRIVATE_KEY_FILE_PATH)) {
    // ssh-keygen creates the private key file and the corresponding .pub file
    try {
      await promisify(execFile)('ssh-keygen', [
        '-q',
        '-b',
        '2048',
        '-t',
        'rsa',
        '-N',
        '',
        '-C',
        'Generated by alwaysAI CLI',
        '-f',
        PRIVATE_KEY_FILE_PATH,
      ]);
      spinner.succeed();
    } catch (exception) {
      spinner.fail();
      throw exception;
    }
  } else {
    const stats = await promisify(stat)(PRIVATE_KEY_FILE_PATH);

    if (!stats.isFile()) {
      spinner.fail();
      throw new TerseError(
        `"${PRIVATE_KEY_FILE_PRETTY_PATH}" exists but is not a file. Please remove it and try again.`,
      );
    }

    if ((stats.mode & 0o700) < 0o400) {
      spinner.fail();
      throw new TerseError(
        `"${PRIVATE_KEY_FILE_PRETTY_PATH}" exists but is not user-readable. Please remove it or fix its permissions`,
      );
    }

    if ((stats.mode & 0o077) !== 0) {
      spinner.fail();
      throw new TerseError(
        `"${PRIVATE_KEY_FILE_PRETTY_PATH}" exists but is group- or world-readable. Please remove it or fix its permissions.`,
      );
    }

    const keyMaterial = await promisify(readFile)(PRIVATE_KEY_FILE_PATH, {
      encoding: 'utf8',
    });

    if (!keyMaterial.startsWith('-----BEGIN OPENSSH PRIVATE KEY-----')) {
      spinner.fail();
      throw new TerseError(
        `"${PRIVATE_KEY_FILE_PRETTY_PATH}" exists but is not an OpenSSH private key. Please remove it and try again.`,
      );
    }

    // Make sure here that the .pub half of the key is in place
    if (!existsSync(PUBLIC_KEY_FILE_PATH)) {
      const { stdout } = await promisify(execFile)('ssh-keygen', [
        '-y',
        '-f',
        PRIVATE_KEY_FILE_PATH,
      ]);

      await promisify(writeFile)(PUBLIC_KEY_FILE_PATH, stdout);
    }
    spinner.succeed(`Found private key file "${PRIVATE_KEY_FILE_PRETTY_PATH}"`);
  }
}
