import { logger, PromiseExecutor } from '@nx/devkit'
import { GenerateSourcesExecutorSchema } from './schema'
import { mkdirSync } from 'node:fs'
import { spawn } from 'node:child_process'
import { deleteOutputDir } from '../utils'

const runExecutor: PromiseExecutor<GenerateSourcesExecutorSchema> = async (options, context) => {
	const outputDir = context.projectsConfigurations.projects[options.outputProjectName].sourceRoot
	const root = context.root

	logger.info(`Deleting outputDir ${outputDir}...`)

	deleteOutputDir(root, outputDir)

	logger.info(`Done deleting outputDir ${outputDir}.`)

	await generateSources(
		options.sourceSpecPath,
		options.sourceSpecUrlAuthorizationHeaders,
		options.generator,
		outputDir,
		options.additionalProperties,
		options.globalProperties,
		options.typeMappings,
		options.silent,
	)

	return { success: true }
}

async function generateSources(
	apiSpecPathOrUrl: string,
	apiSpecAuthorizationHeaders: string,
	generator: string,
	outputDir: string,
	additionalProperties?: string,
	globalProperties?: string,
	typeMappings?: string,
	silent?: boolean,
): Promise<number> {
	mkdirSync(outputDir, { recursive: true })

	return new Promise((resolve, reject) => {
		const { command, args } = { command: 'npx', args: ['openapi-generator-cli'] }

		args.push('generate', '-i', apiSpecPathOrUrl, '-g', generator, '-o', outputDir)

		if (additionalProperties) {
			args.push('--additional-properties', additionalProperties)
		}

		if (apiSpecAuthorizationHeaders) {
			args.push('--auth', apiSpecAuthorizationHeaders)
		}

		if (typeMappings) {
			args.push('--type-mappings', typeMappings)
		}

		if (globalProperties) {
			args.push('--global-property', globalProperties)
		}

		logger.info(`[command]: ${command} ${args.join(' ')}`)

		const child = spawn(command, args, { stdio: silent ? 'ignore' : 'pipe' })

		child.stdout?.on('data', (data) => {
			logger.info(`[stdout]: ${data}`)
		})

		child.stderr?.on('data', (data) => {
			logger.error(`[stderr]: ${data}`)
		})

		child.on('error', (err) => {
			reject(err)
		})

		child.on('exit', (code) => {
			if (code === 0) {
				resolve(code)
			} else {
				reject(code)
			}
		})
	})
}

export default runExecutor
