import { Logger } from '@nestjs/common'
import { context } from '@opentelemetry/api'
import { Job } from 'bullmq'
import tracer from 'dd-trace'
import { getParentSpan, setSpanMetadata } from './utils'

// Decorator for processors to enable Datadog tracing
export const TracedProcessor =
	() => (target: unknown, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {
		const originalMethod = descriptor.value

		descriptor.value = async function (...args: unknown[]) {
			const job = args[0] as Job

			await new Promise<void>((resolve, reject) => {
				tracer.trace(
					'workerJob',
					{
						childOf: getParentSpan(job),
						tags: {
							'resource.name': `process_${job.name}`,
							'job.queue': job.queueName,
							'span.kind': 'worker',
							'sampling.priority': '1',
						},
					},
					async (span) => {
						const logger = new Logger(`${target?.constructor?.name || 'generic'}.${String(propertyKey)}`)

						setSpanMetadata(job.name, span)

						span?.addTags({
							'job.name': job.name,
							'job.id': job.id,
							'job.queue': job.queueName,
							'processor.name': target?.constructor?.name || 'unknown',
							'resource.type': 'bullmq.job',
							'sampling.priority': '1', // force sampling this trace
						})

						logger.log(`Executing traced processor '${job.name}' (id: ${job.id}, queue: ${job.queueName})`)

						try {
							await context.with(context.active(), async () => {
								const result = await originalMethod.apply(this, args)
								resolve(result)
							})
						} catch (error) {
							logger.error(error)
							span?.setTag('error', true)
							span?.setTag('error.msg', error.message)
							span?.setTag('error.type', error.name)
							span?.setTag('error.stack', error.stack)
							reject(error) // Ensure errors are propagated for BullMQ to handle retries
						}
					},
				)
			})
		}

		return descriptor
	}
