import { promisify } from 'util';
import { dirname } from 'path';
import { createWriteStream, rename } from 'fs';
import mkdirp = require('mkdirp');
import pump = require('pump');
import rimraf from 'rimraf';
import { CliTerseError } from '@alwaysai/alwayscli';

import { RandomString } from './random-string';
import { PLEASE_REPORT_THIS_ERROR_MESSAGE } from '../constants';
import { logger } from './logger';
import { stringifyError } from './stringify-error';
import { Readable } from 'node:stream';
import { fetchFilestream } from './fetch';

async function writeToFile(path: string, readable: Readable) {
  mkdirp.sync(dirname(path));
  const tmpFilePath = `${path}.${RandomString()}.tmp`;
  try {
    await new Promise<void>((resolve, reject) => {
      const writeable = createWriteStream(tmpFilePath);
      pump(readable, writeable, (err) => {
        if (err) {
          reject(err);
        } else {
          resolve();
        }
      });
    });
    await promisify(rename)(tmpFilePath, path);
  } catch (exception) {
    try {
      await rimraf(tmpFilePath);
    } catch (err) {
      logger.warn('Failed to delete %s! %s', tmpFilePath, stringifyError(err));
    }
    throw exception;
  }
}

export async function downloadToFile(props: { url: string; path: string }) {
  const { url, path } = props;
  logger.debug(`Downloading package from ${url}`);
  try {
    const filestream = await fetchFilestream(url);
    await writeToFile(path, filestream);
  } catch (e) {
    throw new CliTerseError(
      `Failed to download ${url}. ${PLEASE_REPORT_THIS_ERROR_MESSAGE}`
    );
  }
}
