import { logger, PromiseExecutor } from '@nx/devkit'
import { execSync } from 'node:child_process'
import { cp, glob, rm } from 'node:fs/promises'
import { basename, join } from 'node:path'
import { GenerateExecutorSchema } from './schema'

const runExecutor: PromiseExecutor<GenerateExecutorSchema> = async (
	{ srcProject, copyTo, copyFrom, options },
	context,
) => {
	try {
		// Check if the source project exists in the project graph
		if (!(srcProject in context.projectGraph.nodes)) {
			throw new Error(`Root directory of source project "${srcProject}" not found.`)
		}

		const protoRoot = join(context.projectGraph.nodes[srcProject].data.sourceRoot)
		const targetProjectRoot = join(context.root, context.projectGraph.nodes[context.projectName].data.root)

		// Set the current working directory to the root directory of the source project
		const cwd = join(context.root, protoRoot)

		// Build the 'buf generate' command to be run
		let command = `buf generate`
		if (typeof options === 'string') command += ` ${options}`

		// Run the 'buf generate' command in the current working directory
		if (context.isVerbose) {
			logger.info(`running '${command}' on ${cwd}...`)
		}

		execSync(command, { cwd })

		// Remove the existing generated files in the target project directory
		const copyToPath = join(targetProjectRoot, copyTo)
		if (context.isVerbose) {
			logger.info(`cleaning up ${copyToPath}`)
		}
		await rm(copyToPath, { recursive: true, force: true })

		// Get the list of generated files from the source project directory
		const targetGeneratedFles = glob(copyFrom.map((path) => join(cwd, path)))

		// Copy the generated files to the target project directory
		for await (const file of targetGeneratedFles) {
			const targetFile = join(copyToPath, basename(file))
			if (context.isVerbose) {
				logger.info(`copying ${file} to ${targetFile}`)
			}
			await cp(file, targetFile, { recursive: true, force: true })
		}

		return { success: true }
	} catch (error) {
		logger.error(error)
		return { success: false, error }
	}
}

export default runExecutor
