import { createWriteStream } from 'fs';
import { basename, join } from 'path';
import { CliTerseError } from '@alwaysai/alwayscli';
import { checkUserIsLoggedInComponent } from '../user';
import { getProjectReleases } from '../../core/release/get-release-versions';
import {
  fetchFilestream,
  logger,
  runWithSpinner,
  stringifyError
} from '../../util';
import { fetchAppReleaseHistory, getReleaseURL } from '../../infrastructure';
import { validateIsUserProject } from '../../core/project';
import { ALWAYSAI_CLI_EXECUTABLE_NAME } from '../../constants';

export async function fetchAppReleaseHistoryComponent(props: {
  yes: boolean;
  project: string;
}) {
  const { yes, project } = props;

  await checkUserIsLoggedInComponent({ yes });

  await validateIsUserProject({ project });
  return await fetchAppReleaseHistory(project);
}

export async function appReleasePullComponent(props: {
  yes: boolean;
  project: string;
  releaseHash?: string;
}) {
  const { yes, project, releaseHash } = props;

  await checkUserIsLoggedInComponent({ yes });
  await validateIsUserProject({ project });

  // verify the project has been published (also verifies project ownership)
  const releaseHistory = await fetchAppReleaseHistory(project);
  if (releaseHistory.length === 0) {
    throw new CliTerseError('This project has not been published!');
  }

  // verify valid release hash, if provided
  if (releaseHash !== undefined) {
    const releases = await getProjectReleases({ releaseHistory });
    if (!releases.includes(releaseHash)) {
      throw new CliTerseError(
        `The release hash you specified ${releaseHash} is invalid for this project! To get a list of valid hashes, run \`${ALWAYSAI_CLI_EXECUTABLE_NAME} release list --project ${project}\`
        `
      );
    }
  }

  await runWithSpinner(
    async () => {
      let key;
      let presignedAppUrl;
      try {
        const results = await getReleaseURL(project, releaseHash);
        key = results.key;
        presignedAppUrl = results.presignedAppUrl;
      } catch (error) {
        logger.error(stringifyError(error));
        throw new CliTerseError('An error occurred retrieving project.');
      }
      const response = await fetchFilestream(presignedAppUrl);
      const localDest = join(process.cwd(), `${basename(key)}`);
      const stream = response.pipe(createWriteStream(localDest));

      await new Promise((resolve, reject) => {
        stream.on('finish', resolve);
        stream.on('error', reject);
      });
    },
    [],
    'Retrieving application package'
  );

  return releaseHash;
}
