import { Logger } from '@bpinternal/log4bot'
import yargs, { YargsConfig, YargsSchema } from '@bpinternal/yargs-extra'
import * as consts from '../src/consts'
import { requiredBotSecrets } from './tests/bot-secrets'
import { createDeployBot } from './tests/create-deploy-bot'
import { createDeployBotWithDependencies } from './tests/create-deploy-bot-with-dependencies'
import { createDeployIntegration } from './tests/create-deploy-integration'
import { devBot } from './tests/dev-bot'
import { installAllInterfaces } from './tests/install-interfaces'
import {
  addIntegration,
  addPlugin,
  addLocalIntegrationKeepsRelativePath,
  addDevIntegrationSkipsBpDependencies,
  addDevIntegrationPreservesExistingBpDependency,
  reinstallLocalIntegration,
} from './tests/install-package'
import { requiredIntegrationSecrets } from './tests/integration-secrets'
import { enforceWorkspaceHandle, prependWorkspaceHandle } from './tests/manage-workspace-handle'
import { removePackage } from './tests/remove-package'
import { Test } from './typings'
import { sleep, TmpDirectory } from './utils'

const tests: Test[] = [
  createDeployBot,
  createDeployBotWithDependencies,
  createDeployIntegration,
  devBot,
  requiredBotSecrets,
  requiredIntegrationSecrets,
  prependWorkspaceHandle,
  enforceWorkspaceHandle,
  addIntegration,
  addPlugin,
  addLocalIntegrationKeepsRelativePath,
  addDevIntegrationSkipsBpDependencies,
  addDevIntegrationPreservesExistingBpDependency,
  reinstallLocalIntegration,
  installAllInterfaces,
  removePackage,
]

const timeout = (ms: number) =>
  sleep(ms).then(() => {
    throw new Error(`Timeout after ${ms}ms`)
  })

const TIMEOUT = 300_000

const configSchema = {
  timeout: {
    type: 'number',
    default: TIMEOUT,
  },
  verbose: {
    type: 'boolean',
    default: false,
    alias: 'v',
  },
  filter: {
    type: 'string',
  },
  workspaceId: {
    type: 'string',
    demandOption: true,
  },
  workspaceHandle: {
    type: 'string',
    demandOption: true,
  },
  token: {
    type: 'string',
    demandOption: true,
  },
  apiUrl: {
    type: 'string',
    default: consts.defaultBotpressApiUrl,
  },
  tunnelUrl: {
    type: 'string',
    default: consts.defaultTunnelUrl,
  },
  sdkPath: {
    type: 'string',
    description: 'Path to the Botpress SDK to install; Allows using a version not released on NPM yet.',
  },
  clientPath: {
    type: 'string',
    description: 'Path to the Botpress Client to install; Allows using a version not released on NPM yet.',
  },
} satisfies YargsSchema

const main = async (argv: YargsConfig<typeof configSchema>): Promise<never> => {
  const logger = new Logger('e2e', { level: argv.verbose ? 'debug' : 'info' })

  const filterRegex = argv.filter ? new RegExp(argv.filter) : null
  const filteredTests = tests.filter(({ name }) => (filterRegex ? filterRegex.test(name) : true))
  logger.info(`Running ${filteredTests.length} / ${tests.length} tests`)

  const dependencies = { '@botpress/sdk': argv.sdkPath, '@botpress/client': argv.clientPath } satisfies Record<
    string,
    string | undefined
  >

  for (const { name, handler } of filteredTests) {
    const logLine = `### Running test: "${name}" ###`
    const logPad = '#'.repeat(logLine.length)
    logger.info(logPad)
    logger.info(logLine)
    logger.info(logPad + '\n')

    const loggerNamespace = name.replace(/ /g, '_').replace(/[^a-zA-Z0-9_]/g, '')

    const tmpDir = TmpDirectory.create()
    try {
      const t0 = Date.now()
      await Promise.race([
        handler({ tmpDir: tmpDir.path, dependencies, logger: logger.sub(loggerNamespace), ...argv }),
        timeout(argv.timeout),
      ])
      const t1 = Date.now()
      logger.info(`SUCCESS: "${name}" (${t1 - t0}ms)`)
    } catch (thrown) {
      const err = thrown instanceof Error ? thrown : new Error(`${thrown}`)
      logger.attachError(err).error(`FAILURE: "${name}"`)
      process.exit(1)
    } finally {
      tmpDir.cleanup()
    }
  }

  logger.info('All tests passed')
  process.exit(0)
}

void yargs.command('$0', 'Run E2E Tests', configSchema, main).parse()
