/*
 *  This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
 *  License: AGPLv3 s.t. "Commons Clause" – see LICENSE.md for details
 */

import { writeFile, readFile, unlink } from "fs";
import { file } from "tmp";
import { callback } from "awaiting";
import { spawn } from "child_process";



interface ParserOptions {
  parser?: string;
  tabWidth?: number;
  useTabs?: boolean;
  util?: string;
}

function close(proc, cb): void {
  proc.on("close", (code) => cb(undefined, code));
}

// TODO: diversify this via options to support autopep8, black (requires python 3.6), and others...

function yapf(input_path) {
  return spawn("yapf", ["-i", input_path]);
}

// from a full stacktrace, only show user the last line (encodes some reason and line number) ... everything else does not help.
function tail(str?: string, lines = 4): string {
  if (str == null) {
    return "Problem running formatter.";
  } else {
    return (
      str
        .trim()
        .split(/\r?\n/)
        .slice(-lines)
        .filter((x) => x.trim().length > 0)
        .join("\n") || ""
    );
  }
}

export async function python_format(
  input: string,
  options: ParserOptions,
  logger: any
): Promise<string> {
  // create input temp file
  const input_path: string = await callback(file);
  try {
    await callback(writeFile, input_path, input);

    // spawn the python formatter
    const util = options.util || "yapf";

    if (util !== "yapf") {
      throw new Error(
        "This project only supports 'yapf' for formatting Python"
      );
    }

    const py_formatter = yapf(input_path);

    py_formatter.on("error", (err) => {
      // ATTN do not throw an error here, because this is triggered by the subprocess!
      logger.debug(
        `Formatting utility exited with error no ${(err as any).errno}`
      );
    });

    // stdout/err capture
    let stdout: string = "";
    let stderr: string = "";
    // read data as it is produced.
    py_formatter.stdout.on("data", (data) => (stdout += data.toString()));
    py_formatter.stderr.on("data", (data) => (stderr += data.toString()));
    // wait for subprocess to close.
    const code = await callback(close, py_formatter);
    // only last line
    // stdout = last_line(stdout);
    if (code) {
      if (code === -2) {
        // ENOENT
        throw new Error(`Formatting utility "${util}" is not installed`);
      }
      stderr = tail(stderr);
      const err_msg = `Python formatter "${util}" exited with code ${code}:\n${stdout}\n${stderr}`;
      logger.debug(`format python error: ${err_msg}`);
      throw new Error(err_msg);
    }

    // all fine, we read from the temp file
    const output: Buffer = await callback(readFile, input_path);
    const s: string = output.toString("utf-8");
    return s;
  } finally {
    unlink(input_path, () => {});
  }
}
