import { createTestNode, createTestWorkflow } from '@/__tests__/mocks';
import { createComponentRenderer } from '@/__tests__/render';
import InputPanel, { type Props } from './InputPanel.vue';
import { createTestingPinia } from '@pinia/testing';
import { waitFor } from '@testing-library/vue';
import {
	createRunExecutionData,
	NodeConnectionTypes,
	type IConnections,
	type INodeExecutionData,
	type IRunData,
} from 'n8n-workflow';
import { setActivePinia } from 'pinia';
import { computed, shallowRef } from 'vue';
import { WorkflowDocumentStoreKey, WorkflowIdKey } from '@/app/constants/injectionKeys';

import {
	injectWorkflowDocumentStore,
	useWorkflowDocumentStore,
	createWorkflowDocumentId,
} from '@/app/stores/workflowDocument.store';
import { useWorkflowExecutionStateStore } from '@/app/stores/workflowExecutionState.store';

vi.mock('@/app/stores/workflowDocument.store', async () => {
	const actual = await vi.importActual('@/app/stores/workflowDocument.store');
	return { ...actual, injectWorkflowDocumentStore: vi.fn() };
});

vi.mock('vue-router', () => {
	return {
		useRouter: () => ({}),
		useRoute: () => ({ meta: {}, params: {} }),
		RouterLink: vi.fn(),
	};
});

const nodes = [
	createTestNode({ id: 'node1', name: 'Node 1' }),
	createTestNode({ id: 'node2', name: 'Node 2' }),
	createTestNode({ name: 'Agent' }),
	createTestNode({ name: 'Tool' }),
];

const render = (props: Partial<Props> = {}, pinData?: INodeExecutionData[], runData?: IRunData) => {
	const connections: IConnections = {
		[nodes[0].name]: {
			[NodeConnectionTypes.Main]: [
				[{ node: nodes[1].name, type: NodeConnectionTypes.Main, index: 0 }],
			],
		},
		[nodes[1].name]: {
			[NodeConnectionTypes.Main]: [
				[{ node: nodes[2].name, type: NodeConnectionTypes.Main, index: 0 }],
			],
		},
		[nodes[3].name]: {
			[NodeConnectionTypes.AiMemory]: [
				[{ node: nodes[2].name, type: NodeConnectionTypes.AiMemory, index: 0 }],
			],
		},
	};

	const pinia = createTestingPinia({
		stubActions: false,
	});
	setActivePinia(pinia);

	const workflow = createTestWorkflow({ nodes, connections });

	const workflowDocumentStore = useWorkflowDocumentStore(createWorkflowDocumentId(workflow.id));
	workflowDocumentStore.hydrate(workflow);

	vi.mocked(injectWorkflowDocumentStore).mockReturnValue(shallowRef(workflowDocumentStore));

	if (pinData) {
		workflowDocumentStore.setPinData(Object.fromEntries(nodes.map((n) => [n.name, pinData])));
	}

	if (runData) {
		// The component reads run data via `injectWorkflowExecutionStateStore()`,
		// which resolves through the execution-state store keyed by the injected
		// workflow document's id.
		useWorkflowExecutionStateStore(createWorkflowDocumentId(workflow.id)).setWorkflowExecutionData({
			id: '',
			workflowData: {
				id: '',
				name: '',
				active: false,
				activeVersionId: null,
				isArchived: false,
				createdAt: '',
				updatedAt: '',
				nodes,
				connections,
				versionId: '',
			},
			finished: false,
			mode: 'trigger',
			status: 'success',
			startedAt: new Date(),
			createdAt: new Date(),
			data: createRunExecutionData({
				resultData: { runData },
			}),
		});
	}

	return createComponentRenderer(InputPanel, {
		props: {
			pushRef: 'pushRef',
			runIndex: 0,
			currentNodeName: nodes[0].name,
			activeNodeName: nodes[1].name,
			displayMode: 'schema',
			focusedMappableInput: '',
			isMappingOnboarded: false,
		},
		global: {
			provide: {
				[WorkflowIdKey as unknown as string]: computed(() => workflow.id),
				[WorkflowDocumentStoreKey as symbol]: shallowRef(workflowDocumentStore),
			},
			stubs: {
				InputPanelPinButton: { template: '<button data-test-id="ndv-pin-data"></button>' },
			},
		},
	})({ props });
};

describe('InputPanel', () => {
	it('should render', async () => {
		const { container, queryByTestId } = render({}, [{ json: { name: 'Test' } }]);

		await waitFor(() => expect(queryByTestId('ndv-data-size-warning')).not.toBeInTheDocument());
		expect(container).toMatchSnapshot();
	});

	it("opens mapping tab by default if the node hasn't run yet", async () => {
		const { findByTestId } = render({ activeNodeName: 'Tool' });

		expect((await findByTestId('radio-button-mapping')).parentNode).toBeChecked();
		expect((await findByTestId('radio-button-debugging')).parentNode).not.toBeChecked();
	});

	it('opens debugging tab by default if the node has already run', async () => {
		const { findByTestId } = render({ activeNodeName: 'Tool' }, undefined, {
			Tool: [
				{
					startTime: 0,
					executionTime: 0,
					executionIndex: 0,
					source: [],
					data: {},
				},
			],
		});

		expect((await findByTestId('radio-button-mapping')).parentNode).not.toBeChecked();
		expect((await findByTestId('radio-button-debugging')).parentNode).toBeChecked();
	});
});
