import chalk from 'chalk';
import figlet from 'figlet';
import { mkdir } from 'node:fs/promises';
import { fromError } from 'zod-validation-error';
import { folderExists } from '../utils/fs.utils.js';
import { initIAppWorkspace } from '../utils/initIAppWorkspace.js';
import { getSpinner } from '../cli-helpers/spinner.js';
import { handleCliError } from '../cli-helpers/handleCliError.js';
import { generateWallet } from '../utils/generateWallet.js';
import * as color from '../cli-helpers/color.js';
import { hintBox } from '../cli-helpers/box.js';
import { projectNameSchema } from '../utils/iAppConfigFile.js';
import { TEMPLATES, type TemplateName } from '../config/config.js';

const targetDir = 'hello-world';

export async function init() {
  const spinner = getSpinner();
  try {
    spinner.start('Configuring project...');
    spinner.log(
      chalk.magenta(
        figlet.textSync('IAPP', {
          font: 'Standard',
          horizontalLayout: 'default',
          verticalLayout: 'default',
        })
      )
    );

    const { projectName } = await spinner.prompt({
      type: 'text',
      name: 'projectName',
      message: `What's your project name? ${color.promptHelper('(A folder with this name will be created)')}`,
      initial: targetDir,
      validate: (value) => {
        try {
          projectNameSchema.parse(value);
          return true;
        } catch (e) {
          return fromError(e)
            .details.map((issue) => issue.message)
            .join('; ');
        }
      },
    });

    if (await folderExists(projectName)) {
      throw Error(
        `Target directory "${projectName}" already exists. Remove it or choose a different name.`
      );
    }

    const INIT_BASIC = 'basic';
    const INIT_ADVANCED = 'advanced';

    const { template, initType } = await spinner.prompt([
      {
        type: 'select',
        name: 'template',
        message: 'Which language do you want to use?',
        choices: Object.entries(TEMPLATES).map(([key, val]) => ({
          title: val.title,
          value: key,
          selected: val?.default,
        })),
      },
      {
        type: 'select',
        name: 'initType',
        message: 'What kind of project do you want to init?',
        choices: [
          {
            title: 'Hello World',
            value: INIT_BASIC,
            selected: true,
            description: 'iapp quick start',
          },
          {
            title: 'advanced',
            value: INIT_ADVANCED,
            description: 'more configuration options for advanced users',
          },
        ],
      },
    ]);
    const templateTitle = TEMPLATES[template as TemplateName].title;

    const {
      useArgs = true,
      useProtectedData = true,
      useInputFile = false,
      useRequesterSecret = false,
      useAppSecret = false,
    } = initType === INIT_ADVANCED
      ? await spinner.prompt([
          {
            type: 'confirm',
            name: 'useArgs',
            message: `Would you like to use args inside your iApp? ${color.promptHelper(
              '(args are public positional arguments, args are provided by users that will run your iApp)'
            )}`,
            initial: true,
          },
          {
            type: 'confirm',
            name: 'useInputFile',
            message: `Would you like to use input files inside your iApp? ${color.promptHelper(
              '(input files are public files downloaded from the internet, files urls are provided by user that will run your iApp)'
            )}`,
            initial: false,
          },
          {
            type: 'confirm',
            name: 'useRequesterSecret',
            message: `Would you like to use requester secrets inside your iApp? ${color.promptHelper(
              '(requester secrets are secrets strings, secrets are provided by users that will run your iApp)'
            )}`,
            initial: false,
          },
          {
            type: 'confirm',
            name: 'useProtectedData',
            message: `Would you like to use a protected data inside your iApp? ${color.promptHelper(
              '(protected data a secret file, the protected data is provided by a third party for users that will run your iApp)'
            )}`,
            initial: false,
          },
          {
            type: 'confirm',
            name: 'useAppSecret',
            message: `Would you like to use an app secret inside your iApp? ${color.promptHelper('(app secret is an immutable secret string provisioned once by the iApp owner)')}`,
            initial: false,
          },
        ])
      : {}; // default

    await mkdir(projectName);
    process.chdir(projectName);

    // Copying simple project files from templates/

    spinner.start(`Creating ${templateTitle} app...`);
    await initIAppWorkspace({
      projectName,
      template,
      useArgs,
      useProtectedData,
      useInputFile,
      useRequesterSecret,
      useAppSecret,
    });
    spinner.succeed(`${templateTitle} app setup complete.`);

    spinner.start('Generating wallet...');
    const walletAddress = await generateWallet();
    spinner.succeed(`Generated ethereum wallet (${walletAddress})`);

    const output = `
  ${chalk.bold.underline('Steps to Get Started:')}
  
    Navigate to your project folder:
    ${color.command(`$ cd ${projectName.split(' ').length > 1 ? `"${projectName}"` : `${projectName}`}`)}
  
    ${color.emphasis('Make your changes in the')} ${color.file(TEMPLATES[template as TemplateName]?.mainFile)} ${color.emphasis('file')}.
  
    -1- Test your iApp locally:
    ${color.command('$ iapp test')}${
      useArgs
        ? `
    ${color.comment('# with args')}
    ${color.command('$ iapp test --args your-name')}`
        : ''
    }${
      useInputFile
        ? `
    ${color.comment('# with input files')}
    ${color.command('$ iapp test --inputFile https://ipfs.iex.ec/ipfs/Qmd286K6pohQcTKYqnS1YhWrCiS4gz7Xi34sdwMe9USZ7u')}`
        : ''
    }${
      useRequesterSecret
        ? `
    ${color.comment('# with requester secrets')}
    ${color.command('$ iapp test --requesterSecret 1=foo 42=bar')}`
        : ''
    }${
      useProtectedData
        ? `
    ${color.comment('# with a protected data')}
    ${color.command('$ iapp test --protectedData default')}`
        : ''
    }

    -2- Deploy your iApp on the iExec protocol:
    ${color.command('$ iapp deploy')}
  
    -3- Ask an iExec worker to run your confidential iApp:
    ${color.command('$ iapp run <iapp-address>')}
  `;

    spinner.log(hintBox(output));
  } catch (error) {
    handleCliError({ spinner, error });
  }
}
