import { createCanvasGraphNode } from '@/features/workflows/canvas/__tests__/utils';
import {
	createTestNode,
	createTestNodeProperties,
	mockNodeTypeDescription,
} from '@/__tests__/mocks';
import { createComponentRenderer } from '@/__tests__/render';
import { mockedStore } from '@/__tests__/utils';
import { SET_NODE_TYPE } from '@/app/constants';
import { useFocusPanelStore } from '@/app/stores/focusPanel.store';
import { useNodeTypesStore } from '@/app/stores/nodeTypes.store';
import { useWorkflowsStore } from '@/app/stores/workflows.store';
import {
	injectWorkflowDocumentStore,
	useWorkflowDocumentStore,
	createWorkflowDocumentId,
} from '@/app/stores/workflowDocument.store';
import { createTestingPinia } from '@pinia/testing';
import { useVueFlow } from '@vue-flow/core';
import type { INodeProperties } from 'n8n-workflow';
import { setActivePinia } from 'pinia';
import { reactive, computed, ref, shallowRef } from 'vue';
import { WorkflowIdKey } from '@/app/constants/injectionKeys';
import { useExperimentalNdvStore } from '@/features/workflows/canvas/experimental/experimentalNdv.store';
import { useSetupPanelStore } from '@/features/setupPanel/setupPanel.store';
import FocusSidebar from './FocusSidebar.vue';

// ---- Evaluations experiment + license mocks ----
const mockIsEvaluationsEnabled = ref(false);
vi.mock('@/experiments/evaluationsWizardSidepanel/useEvaluationsWizardSidepanelExperiment', () => ({
	useEvaluationsWizardSidepanelExperiment: () => ({
		isFeatureEnabled: computed(() => mockIsEvaluationsEnabled.value),
	}),
}));

const mockAiRootNodes = ref<string[]>([]);
vi.mock('@/features/ai/evaluation.ee/composables/useAiRootNodes', () => ({
	useAiRootNodes: () => computed(() => mockAiRootNodes.value),
}));

const mockIsLicensed = ref(true);
const mockIsResolved = ref(true);
const mockEnsureLicenseLoaded = vi.fn().mockResolvedValue(undefined);
vi.mock('@/features/ai/evaluation.ee/composables/useEvaluationsLicense', () => ({
	useEvaluationsLicense: () => ({
		isLicensed: computed(() => mockIsLicensed.value),
		isResolved: computed(() => mockIsResolved.value),
		ensureLicenseLoaded: mockEnsureLicenseLoaded,
	}),
}));

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

vi.mock('@/app/composables/useWorkflowId', async () => {
	const { computed } = await import('vue');
	const { useWorkflowsStore } = await import('@/app/stores/workflows.store');
	return {
		useWorkflowId: () => computed(() => useWorkflowsStore().workflowId),
		useRouteWorkflowId: () => computed(() => useWorkflowsStore().workflowId),
	};
});

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

describe('FocusSidebar', () => {
	const renderComponent = createComponentRenderer(FocusSidebar, {
		props: {
			isCanvasReadOnly: false,
		},
		global: {
			provide: {
				[WorkflowIdKey as unknown as string]: computed(() => 'w0'),
			},
		},
	});

	const parameter0: INodeProperties = createTestNodeProperties({
		displayName: 'P0',
		name: 'p0',
		type: 'string',
		default: '',
		description: '',
		validateType: 'string',
	});
	const parameter1: INodeProperties = createTestNodeProperties({
		displayName: 'P1',
		name: 'p1',
		type: 'string',
		default: '',
		description: '',
		validateType: 'string',
	});

	let experimentalNdvStore: ReturnType<typeof mockedStore<typeof useExperimentalNdvStore>>;
	let focusPanelStore: ReturnType<typeof useFocusPanelStore>;
	let nodeTypesStore: ReturnType<typeof useNodeTypesStore>;
	let workflowsStore: ReturnType<typeof useWorkflowsStore>;
	let workflowDocumentStore: ReturnType<typeof useWorkflowDocumentStore>;
	let setupPanelStore: ReturnType<typeof mockedStore<typeof useSetupPanelStore>>;

	const testNodes = [
		createTestNode({ id: 'n0', name: 'N0', parameters: { p0: 'v0' }, type: SET_NODE_TYPE }),
	];

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

		localStorage.clear();

		nodeTypesStore = useNodeTypesStore(pinia);
		nodeTypesStore.setNodeTypes([
			mockNodeTypeDescription({
				name: SET_NODE_TYPE,
				properties: [parameter0, parameter1],
			}),
		]);
		workflowsStore = useWorkflowsStore(pinia);
		workflowsStore.setWorkflowId('w0');

		workflowDocumentStore = useWorkflowDocumentStore(createWorkflowDocumentId('w0'));
		workflowDocumentStore.setNodes(testNodes);
		vi.mocked(injectWorkflowDocumentStore).mockReturnValue(shallowRef(workflowDocumentStore));

		focusPanelStore = useFocusPanelStore(pinia);
		focusPanelStore.toggleFocusPanel();

		setupPanelStore = mockedStore(useSetupPanelStore);
		setupPanelStore.isFeatureEnabled = false;

		experimentalNdvStore = mockedStore(useExperimentalNdvStore);
		experimentalNdvStore.isNdvInFocusPanelEnabled = true;

		// Reset evaluations-related mocks.
		mockIsEvaluationsEnabled.value = false;
		mockAiRootNodes.value = [];
		mockIsLicensed.value = true;
		mockIsResolved.value = true;
		mockEnsureLicenseLoaded.mockReset();
		mockEnsureLicenseLoaded.mockResolvedValue(undefined);
	});

	describe('when setup panel feature is enabled', () => {
		beforeEach(() => {
			setupPanelStore.isFeatureEnabled = true;
		});

		it('should render the setup panel tabs', async () => {
			const rendered = renderComponent({});

			expect(await rendered.findByTestId('setup-panel-tabs')).toBeInTheDocument();
		});

		it('should render the setup panel content by default', async () => {
			const rendered = renderComponent({});

			expect(await rendered.findByTestId('setup-panel-container')).toBeInTheDocument();
		});

		it('should switch to focus tab when a parameter is focused', async () => {
			const rendered = renderComponent({});

			// Initially shows setup panel
			expect(await rendered.findByTestId('setup-panel-container')).toBeInTheDocument();

			// Focus a parameter
			focusPanelStore.openWithFocusedNodeParameter({
				nodeId: 'n0',
				parameter: parameter0,
				parameterPath: 'parameters.p0',
			});

			// Should now show the focus parameter view
			expect(await rendered.findByTestId('focus-parameter')).toBeInTheDocument();
			expect(rendered.queryByTestId('setup-panel-container')).not.toBeInTheDocument();
		});

		it('should show parameter displayName as focus tab label when parameter is focused', async () => {
			const rendered = renderComponent({});

			focusPanelStore.openWithFocusedNodeParameter({
				nodeId: 'n0',
				parameter: parameter0,
				parameterPath: 'parameters.p0',
			});

			await rendered.findByTestId('focus-parameter');

			const tabs = rendered.getByTestId('setup-panel-tabs');
			expect(tabs).toHaveTextContent('P0');
		});

		it('should show node name as focus tab label when node is selected but no parameter is focused', async () => {
			const graphNode = createCanvasGraphNode({ id: 'n0' });
			const vueFlow = useVueFlow('w0');
			const rendered = renderComponent({});

			vueFlow.addNodes([graphNode]);
			vueFlow.addSelectedNodes([graphNode]);

			await rendered.findByTestId('setup-panel-tabs');

			const tabs = rendered.getByTestId('setup-panel-tabs');
			expect(tabs).toHaveTextContent('N0');
		});
	});

	describe('evaluations wizard sidepanel', () => {
		beforeEach(() => {
			mockIsEvaluationsEnabled.value = true;
			// Simulate an AI root node being present.
			mockAiRootNodes.value = ['ai-agent-1'];
			focusPanelStore.selectedTab = 'evaluations';
		});

		it('shows the evaluations wizard when licensed, resolved, AI node present, evaluations tab selected', async () => {
			mockIsLicensed.value = true;
			mockIsResolved.value = true;
			const rendered = renderComponent({});
			// EvaluationsWizardSidepanel is rendered — its stub element appears.
			// We can't query a test-id since the child components aren't rendered in
			// unit tests, but the paywall should NOT be present.
			expect(rendered.queryByTestId('evaluations-unlicensed')).not.toBeInTheDocument();
		});

		it('shows the evaluations paywall when unlicensed and resolved', async () => {
			mockIsLicensed.value = false;
			mockIsResolved.value = true;
			const rendered = renderComponent({});
			expect(rendered.queryByTestId('evaluations-unlicensed')).toBeInTheDocument();
		});

		it('shows neither wizard nor paywall while license is not yet resolved (unresolved state)', async () => {
			mockIsResolved.value = false;
			mockIsLicensed.value = false;
			const rendered = renderComponent({});
			expect(rendered.queryByTestId('evaluations-unlicensed')).not.toBeInTheDocument();
		});

		it('calls ensureLicenseLoaded on mount', async () => {
			const rendered = renderComponent({});
			// Give Vue a tick to call onMounted.
			await rendered.findByTestId('focus-sidebar');
			expect(mockEnsureLicenseLoaded).toHaveBeenCalled();
		});
	});
});
