import { env } from "../env.js";
import { getLogger } from "../logger.js";
import { createLocalWorld } from "@workflow/world-local";
import { createWorld } from "@workflow/world-postgres";
import { createVercelWorld } from "@workflow/world-vercel";

//#region src/workflow/world.ts
/**
* Workflow world configuration.
*
* Static imports are required instead of dynamic imports because
* Vercel's NFT can't trace dynamic imports in bundled code.
*
* Set WORKFLOW_TARGET_WORLD to: 'local' | 'vercel' | '@workflow/world-postgres'
* Defaults to 'local' for development if not set.
*/
const logger = getLogger("workflow-world");
const targetWorld = env.WORKFLOW_TARGET_WORLD || "local";
let world;
if (targetWorld === "vercel") {
	const token = process.env.WORKFLOW_VERCEL_AUTH_TOKEN;
	world = createVercelWorld({
		token: token?.trim() || void 0,
		projectConfig: {
			projectId: process.env.VERCEL_PROJECT_ID,
			teamId: process.env.VERCEL_TEAM_ID,
			environment: process.env.VERCEL_ENV
		}
	});
} else if (targetWorld === "@workflow/world-postgres") world = createWorld({
	connectionString: env.WORKFLOW_POSTGRES_URL || "postgres://world:world@localhost:5432/world",
	jobPrefix: env.WORKFLOW_POSTGRES_JOB_PREFIX,
	queueConcurrency: Number(env.WORKFLOW_POSTGRES_WORKER_CONCURRENCY) || 10
});
else world = createLocalWorld();
/**
* Check if the current world supports orphan recovery.
* Both postgres and local worlds support recovery (they both have runs.list and queue).
* Vercel world does not need recovery as it handles this differently.
*/
function supportsOrphanRecovery() {
	return targetWorld === "@workflow/world-postgres" || targetWorld === "local";
}
/**
* Re-enqueue a workflow run by its ID.
* This creates a new job in the queue for an existing run.
*
* Use this to recover orphaned workflows that are stuck in "running" state
* but have no corresponding job in the queue (e.g., after server restart).
*/
async function reenqueueRun(runId) {
	if (!supportsOrphanRecovery()) {
		logger.warn({ targetWorld }, "reenqueueRun is not supported for this world");
		throw new Error(`reenqueueRun is not supported for ${targetWorld}`);
	}
	try {
		const run = await world.runs.get(runId);
		if (!run) throw new Error(`Run not found: ${runId}`);
		const { workflowName, deploymentId } = run;
		const queueName = `__wkf_workflow_${workflowName}`;
		logger.info({
			runId,
			workflowName,
			queueName,
			deploymentId
		}, "Re-enqueueing workflow run");
		await world.queue(queueName, { runId }, { deploymentId });
	} catch (error) {
		logger.error({
			runId,
			error: error instanceof Error ? error.message : String(error)
		}, "Failed to re-enqueue workflow run");
		throw error;
	}
}
/**
* Re-enqueue a workflow run directly using run data (without re-fetching).
* This is more efficient when we already have the run data from a list operation.
*/
async function reenqueueRunDirect(runId, workflowName, deploymentId) {
	const queueName = `__wkf_workflow_${workflowName}`;
	await world.queue(queueName, { runId }, { deploymentId });
}
async function recoverRun(run) {
	try {
		await reenqueueRunDirect(run.runId, run.name, run.deploymentId);
		return true;
	} catch (error) {
		logger.warn({
			runId: run.runId,
			error: error instanceof Error ? error.message : String(error)
		}, "Failed to recover workflow run");
		return false;
	}
}
async function recoverOrphanedWorkflows() {
	if (!supportsOrphanRecovery()) {
		logger.info({ targetWorld }, "Orphan recovery skipped - not supported for this world");
		return 0;
	}
	try {
		logger.info({ targetWorld }, "Checking for orphaned workflow runs...");
		let recoveredCount = 0;
		let cursor;
		do {
			const result = await world.runs.list({
				status: "running",
				pagination: {
					limit: 100,
					cursor
				},
				resolveData: "none"
			});
			if (!result.data?.length) break;
			for (const run of result.data) if (await recoverRun(run)) recoveredCount++;
			cursor = result.cursor;
		} while (cursor);
		logger.info({ recoveredCount }, recoveredCount > 0 ? "Finished recovering orphaned workflows" : "No orphaned workflows found");
		return recoveredCount;
	} catch (error) {
		logger.error({ error: error instanceof Error ? error.message : String(error) }, "Failed to list orphaned workflows");
		return 0;
	}
}

//#endregion
export { recoverOrphanedWorkflows, reenqueueRun, world };