import { writeFile } from 'node:fs/promises'
import path from 'node:path'
import { cwd } from 'node:process'
import { input, select } from '@inquirer/prompts'
import { Args, Command } from '@oclif/core'
import { loadConfig } from '@vtex/fsp-config'

import { availableModules, load, moduleCliMap } from '../modules.js'

export class Create extends Command {
  static ACCOUNT_PROMPT = 'What is the account name?'
  static MODULE_PROMPT = 'Which module do you want to initialize?'
  static PATH_PROMPT = (moduleName: string) => {
    return `What should be the path to initialize ${moduleName}?`
  }

  static args = {
    account: Args.string({
      required: false,
      description: 'Name of the account to be initialized',
    }),

    moduleName: Args.string({
      required: false,
      description: 'Name of the module to be initialized',
    }),

    path: Args.string({
      required: false,
      description: 'Path of where to initialize the module',
    }),
  }

  static description = 'Add a new faststore module on the monorepo.'

  static examples = ['<%= config.bin %> <%= command.id %>']

  static flags = {}

  async run(): Promise<void> {
    const { args } = await this.parse(Create)

    let account = args.account

    if (!account) {
      account = await input({ message: Create.ACCOUNT_PROMPT })
    }

    let moduleName = args.moduleName

    if (!moduleName) {
      moduleName = await select({
        message: Create.MODULE_PROMPT,
        choices: availableModules.map((module) => ({
          name: module,
          value: module,
        })),
      })
    }

    let modulePath = args.path

    if (!modulePath) {
      modulePath = await input({
        message: Create.PATH_PROMPT(moduleName),
        default: `./packages/${moduleName}`,
      })
    }

    const currentConfig = await loadConfig()

    currentConfig.stores = currentConfig.stores ?? {}

    if (currentConfig.stores?.[account]?.[moduleName]) {
      this.error(`${moduleName} has already been initialized for ${account}.`, {
        code: 'Already Initialized Module',
        exit: 1,
      })
    }

    const accountConfig = currentConfig.stores[account] ?? {}

    const hasModules = Object.keys(accountConfig).length > 0

    /**
     * The module port is initally 3001.
     * We must find the correct port later on.
     */
    let modulePort = 3001

    /**
     * If there are already modules in the store we search for the largest port number available.
     * The new module port will be the largestPort + 1
     */
    if (hasModules) {
      let largestPort = modulePort

      for (const module of Object.keys(accountConfig)) {
        const port = accountConfig[module].port
        if (port && port > largestPort) {
          largestPort = port
        }
      }

      modulePort = largestPort + 1
    }

    currentConfig.stores[account] = {
      ...accountConfig,
      [moduleName]: {
        path: modulePath,
        port: modulePort,
      },
    }

    this.log(`Initializing ${moduleName} for ${account} in ${modulePath}`)

    const cli = await load({ cli: moduleCliMap[moduleName], path: modulePath })

    await cli.commands.create.run([modulePath])

    await writeFile(
      path.join(cwd(), 'faststore.json'),
      `${JSON.stringify(currentConfig, undefined, 2)}\n`
    )
  }
}
