import { createWorkflowWithHistory, setActiveVersion, testDb } from '@n8n/backend-test-utils';
import { WorkflowPublishedVersionRepository } from '@n8n/db';
import { Container } from '@n8n/di';
import type { INode } from 'n8n-workflow';
import { v4 as uuid } from 'uuid';

import { WorkflowPublishedDataService } from '@/workflows/workflow-published-data.service';

import { createOwner } from '../shared/db/users';
import { createWorkflowHistoryItem } from '../shared/db/workflow-history';

let workflowPublishedVersionRepository: WorkflowPublishedVersionRepository;
let workflowPublishedDataService: WorkflowPublishedDataService;

beforeAll(async () => {
	await testDb.init();
	workflowPublishedVersionRepository = Container.get(WorkflowPublishedVersionRepository);
	workflowPublishedDataService = Container.get(WorkflowPublishedDataService);
});

afterAll(async () => {
	await testDb.terminate();
});

const makeNode = (name: string): INode => ({
	id: uuid(),
	name,
	type: 'n8n-nodes-base.noOp',
	typeVersion: 1,
	position: [0, 0],
	parameters: {},
});

describe('WorkflowPublishedDataService', () => {
	test('should read nodes/connections from workflow_published_version table', async () => {
		const owner = await createOwner();
		const workflow = await createWorkflowWithHistory({}, owner);
		await setActiveVersion(workflow.id, workflow.versionId);

		// Write to published version table
		await workflowPublishedVersionRepository.setPublishedVersion(workflow.id, workflow.versionId);

		const result = await workflowPublishedDataService.getPublishedWorkflowData(workflow.id);

		expect(result).not.toBeNull();
		expect(result!.workflow.id).toBe(workflow.id);
		expect(result!.publishedVersion.nodes).toEqual(
			expect.arrayContaining([expect.objectContaining({ type: 'n8n-nodes-base.scheduleTrigger' })]),
		);
	});

	test('should return data from a different version than activeVersion when published_version table points elsewhere', async () => {
		const owner = await createOwner();
		const workflow = await createWorkflowWithHistory({}, owner);
		await setActiveVersion(workflow.id, workflow.versionId);

		// Create a second history version with different nodes
		const alternateVersionId = uuid();
		const alternateNodes = [makeNode('Alternate Node')];
		await createWorkflowHistoryItem(workflow.id, {
			versionId: alternateVersionId,
			nodes: alternateNodes,
			connections: {},
		});

		// Point the published version table to the alternate version
		// (NOT the activeVersion). This proves we're reading from the table.
		await workflowPublishedVersionRepository.setPublishedVersion(workflow.id, alternateVersionId);

		const result = await workflowPublishedDataService.getPublishedWorkflowData(workflow.id);

		expect(result).not.toBeNull();
		// Should have the alternate nodes, NOT the original activeVersion nodes
		expect(result!.publishedVersion.nodes).toEqual(
			expect.arrayContaining([expect.objectContaining({ name: 'Alternate Node' })]),
		);
		expect(result!.publishedVersion.nodes).not.toEqual(
			expect.arrayContaining([expect.objectContaining({ type: 'n8n-nodes-base.scheduleTrigger' })]),
		);
	});

	test('should include node groups in the execution projection', async () => {
		const owner = await createOwner();
		const workflow = await createWorkflowWithHistory({}, owner);
		await setActiveVersion(workflow.id, workflow.versionId);

		const versionId = uuid();
		const nodes = [makeNode('Grouped Node')];
		const nodeGroups = [{ id: 'group-1', name: 'Group 1', nodeIds: [nodes[0].id] }];
		await createWorkflowHistoryItem(workflow.id, {
			versionId,
			nodes,
			connections: {},
			nodeGroups,
		});

		await workflowPublishedVersionRepository.setPublishedVersion(workflow.id, versionId);

		const result = await workflowPublishedDataService.getPublishedWorkflowDataForExecution(
			workflow.id,
		);

		expect(result).not.toBeNull();
		expect(result!.nodeGroups).toEqual(nodeGroups);
	});

	test('should return null when published_version table has no record', async () => {
		const owner = await createOwner();
		const workflow = await createWorkflowWithHistory({}, owner);
		await setActiveVersion(workflow.id, workflow.versionId);
		// Don't write to published version table

		const result = await workflowPublishedDataService.getPublishedWorkflowData(workflow.id);

		expect(result).toBeNull();
	});
});
