import { getAlksAccounts } from './getAlksAccounts';
import { getFavorites } from './getFavorites';
import { getStdErrPrompt } from './getStdErrPrompt';
import { log } from './log';
import { getAlksAccount } from './state/alksAccount';
import { getAlksRole } from './state/alksRole';
import { parseAlksAccount } from './parseAlksAccount';
import { formatAccountOutput } from './formatAccountOutput';
import { compareFavorites } from './compareFavorites';
import { getAccountDelim } from './getAccountDelim';

export interface GetAlksAccountOptions {
  iamOnly: boolean;
  prompt: string;
  filterFavorites: boolean;
}

export interface AlksAccountPromptData {
  type: string;
  name: string;
  message: string;
  source: (
    answers: Record<string, unknown>,
    input: string | undefined
  ) => Promise<string[]>;
  pageSize: number;
  default?: string;
}

export async function promptForAlksAccountAndRole(
  options: Partial<GetAlksAccountOptions>
): Promise<{ alksAccount: string; alksRole: string }> {
  const opts: GetAlksAccountOptions = {
    iamOnly: options.iamOnly || false,
    prompt: options.prompt || 'Please select an ALKS account/role',
    filterFavorites: options.filterFavorites || false,
  };

  const alksAccounts = (await getAlksAccounts({ iamOnly: opts.iamOnly })).map(
    parseAlksAccount
  );

  const favorites = await getFavorites();
  log(`Favorites: ${favorites.toString()}`);

  const maxAccountAliasLength = Math.max(
    ...alksAccounts.map((a) => a.accountAlias.length)
  );
  const maxAccountIdAndRoleLength = Math.max(
    ...alksAccounts.map((a) => a.accountIdAndRole.length)
  );

  const indexedAlksAccounts = alksAccounts
    .filter(
      (alksAccount) =>
        !opts.filterFavorites ||
        favorites.includes(
          [alksAccount.account, alksAccount.role].join(getAccountDelim())
        )
    ) // Filter out non-favorites if filterFavorites flag is passed
    .sort(compareFavorites(favorites)) // Sort favorites to top while maintaining alphabetical order
    .map((alksAccount) => ({
      ...alksAccount,
      formattedOutput: formatAccountOutput(
        alksAccount,
        maxAccountAliasLength,
        maxAccountIdAndRoleLength
      ),
    })); // Add a field to the account object containing the formatted output string

  if (!indexedAlksAccounts.length) {
    throw new Error('No accounts found.');
  }

  const allChoices = indexedAlksAccounts.map((a) => a.formattedOutput);

  const promptData: AlksAccountPromptData = {
    type: 'autocomplete',
    name: 'alksAccount',
    message: opts.prompt,
    source: (
      _answers: Record<string, unknown>,
      input: string | undefined
    ): Promise<string[]> => {
      if (!input) {
        return Promise.resolve(allChoices);
      }
      return Promise.resolve(
        allChoices.filter((c) => c.toLowerCase().includes(input.toLowerCase()))
      );
    },
    pageSize: 15,
  };

  // Ignore failure since we're about to prompt for it
  const defaultAlksAccount = await getAlksAccount();
  const defaultAlksRole = await getAlksRole();

  // If a default account and role are set and they match an account the user has, find the corresponding formatted output string
  if (defaultAlksAccount && defaultAlksRole) {
    const defaultAccount = indexedAlksAccounts.find(
      (account) =>
        account.account === defaultAlksAccount &&
        account.role === defaultAlksRole
    );
    if (defaultAccount) {
      promptData.default = defaultAccount.formattedOutput;
    }
  }

  // ask user which account/role
  const prompt = getStdErrPrompt();
  const answers = await prompt([promptData]);

  const selectedString = answers.alksAccount as string;
  const selectedAccount = indexedAlksAccounts.find(
    (account) => account.formattedOutput === selectedString
  );

  if (!selectedAccount) {
    log(
      `Selected account "${selectedString}" not found in the list of accounts.`
    );
    throw new Error(
      'Account selection failed. The selected account was not found.'
    );
  }

  return {
    alksAccount: selectedAccount.account,
    alksRole: selectedAccount.role,
  };
}
