import React from 'react';
import { QueryClient, QueryClientProvider, useQuery } from 'react-query';
// eslint-disable-next-line no-restricted-imports
import * as ReactRedux from 'react-redux';
import {
	act,
	fireEvent,
	render,
	RenderResult,
	screen,
	waitFor,
} from '@testing-library/react';
import { getFieldValues } from 'api/dynamicVariables/getFieldValues';
import { IDashboardVariable } from 'types/api/dashboard/getAll';

import DynamicVariableInput from '../DashboardVariablesSelection/DynamicVariableInput';

// Mock useVariableFetchState to return "fetching" state so useQuery is enabled
jest.mock('hooks/dashboard/useVariableFetchState', () => ({
	useVariableFetchState: (): Record<string, unknown> => ({
		variableFetchCycleId: 0,
		variableFetchState: 'loading',
		isVariableSettled: false,
		isVariableFetching: true,
		hasVariableFetchedOnce: false,
		isVariableWaitingForDependencies: false,
		variableDependencyWaitMessage: '',
	}),
}));

// Mock the getFieldValues API
jest.mock('api/dynamicVariables/getFieldValues', () => ({
	getFieldValues: jest.fn(),
}));

// Mock useQuery from react-query
jest.mock('react-query', () => {
	const originalModule = jest.requireActual('react-query');
	return {
		...originalModule,
		useQuery: jest.fn(),
	};
});

describe('Dynamic Variable Default Behavior', () => {
	const mockOnValueUpdate = jest.fn();
	const mockApiResponse = {
		httpStatusCode: 200,
		data: {
			values: {
				stringValues: ['frontend', 'backend', 'database', 'cache'],
			},
			normalizedValues: ['frontend', 'backend', 'database', 'cache'],
			complete: true,
		},
	};

	let queryClient: QueryClient;

	const renderWithQueryClient = (component: React.ReactElement): RenderResult =>
		render(
			<QueryClientProvider client={queryClient}>{component}</QueryClientProvider>,
		);

	beforeEach(() => {
		// Mock scrollIntoView for JSDOM environment
		window.HTMLElement.prototype.scrollIntoView = jest.fn();
		jest.clearAllMocks();

		// Create a new QueryClient for each test
		queryClient = new QueryClient({
			defaultOptions: {
				queries: {
					retry: false,
				},
			},
		});

		// Mock getFieldValues API to return our test data
		(getFieldValues as jest.Mock).mockResolvedValue(mockApiResponse);

		// Mock useQuery implementation to avoid infinite re-renders
		// and ensure onSuccess is called once
		(useQuery as jest.Mock).mockImplementation((key, options) => {
			const { onSuccess, enabled, queryFn } = options || {};
			const variableName = key[1];
			const dynamicVarsKey = key[2];

			React.useEffect(() => {
				if (enabled !== false) {
					if (onSuccess) {
						// For 'services' tests:
						// 1. "Default to ALL" expectations imply empty options -> [] behavior. This happens when selectedValue is undefined (dynamicVarsKey has 'null').
						// 2. "ALL Option Special Value" needs full options to render the "ALL" item in dropdown. This happens when selectedValue is defined.
						if (
							variableName === 'services' &&
							typeof dynamicVarsKey === 'string' &&
							dynamicVarsKey.includes('null')
						) {
							onSuccess({
								...mockApiResponse,
								data: { ...mockApiResponse.data, normalizedValues: [] },
							});
						} else {
							onSuccess(mockApiResponse);
						}
					}
					if (queryFn) {
						queryFn({ signal: undefined });
					}
				}
			}, [enabled, variableName, dynamicVarsKey]); // Only depend on enabled/keys

			return {
				isLoading: false,
				isError: false,
				data: mockApiResponse,
				refetch: jest.fn(),
			};
		});

		jest.spyOn(ReactRedux, 'useSelector').mockReturnValue({
			minTime: '2023-01-01T00:00:00Z',
			maxTime: '2023-01-02T00:00:00Z',
		});
	});

	describe('Single Select Default Values', () => {
		it('should use default value when no previous selection exists', async () => {
			const variableData: IDashboardVariable = {
				id: 'var1',
				name: 'service',
				type: 'DYNAMIC',
				multiSelect: false,
				defaultValue: 'backend',
				selectedValue: undefined,
				dynamicVariablesAttribute: 'service.name',
				dynamicVariablesSource: 'Traces',
				description: '',
				sort: 'DISABLED',
				showALLOption: false,
				allSelected: false,
			};

			await act(async () => {
				renderWithQueryClient(
					<DynamicVariableInput
						variableData={variableData}
						existingVariables={{ var1: variableData }}
						onValueUpdate={mockOnValueUpdate}
					/>,
				);
			});

			// Should call onValueUpdate with default value
			expect(mockOnValueUpdate).toHaveBeenCalledWith(
				'service',
				'var1',
				'backend',
				true,
				false,
			);
		});

		it('should preserve previous selection over default value', async () => {
			const variableData: IDashboardVariable = {
				id: 'var1',
				name: 'service',
				type: 'DYNAMIC',
				multiSelect: false,
				defaultValue: 'backend',
				selectedValue: 'frontend',
				dynamicVariablesAttribute: 'service.name',
				dynamicVariablesSource: 'Traces',
				description: '',
				sort: 'DISABLED',
				showALLOption: false,
				allSelected: false,
			};

			await act(async () => {
				renderWithQueryClient(
					<DynamicVariableInput
						variableData={variableData}
						existingVariables={{ var1: variableData }}
						onValueUpdate={mockOnValueUpdate}
					/>,
				);
			});

			// Should NOT call onValueUpdate since previous selection exists
			expect(mockOnValueUpdate).not.toHaveBeenCalledWith();

			// Check if the previous selection 'frontend' is displayed in the UI
			// For single select, the value should be visible in the select component
			const selectElement = screen.getByRole('combobox');
			expect(selectElement).toBeInTheDocument();

			// Open dropdown to check if 'frontend' is selected
			await act(async () => {
				fireEvent.mouseDown(selectElement);
			});

			// Check if 'frontend' option is present and selected in dropdown
			const frontendOption = screen.getByRole('option', { name: 'frontend' });
			expect(frontendOption).toHaveClass('selected');
		});

		it('should use first value when no default and no previous selection', async () => {
			const variableData: IDashboardVariable = {
				id: 'var1',
				name: 'service',
				type: 'DYNAMIC',
				multiSelect: false,
				defaultValue: undefined,
				selectedValue: undefined,
				dynamicVariablesAttribute: 'service.name',
				dynamicVariablesSource: 'Traces',
				description: '',
				sort: 'DISABLED',
				showALLOption: false,
				allSelected: false,
			};

			await act(async () => {
				renderWithQueryClient(
					<DynamicVariableInput
						variableData={variableData}
						existingVariables={{ var1: variableData }}
						onValueUpdate={mockOnValueUpdate}
					/>,
				);
			});

			// Check if the dropdown is present
			const selectElement = screen.getByRole('combobox');
			expect(selectElement).toBeInTheDocument();

			// Wait for API call to complete and data to be loaded
			await waitFor(() => {
				expect(getFieldValues).toHaveBeenCalledWith(
					'traces',
					'service.name',
					'',
					'2023-01-01T00:00:00Z',
					'2023-01-02T00:00:00Z',
					'',
					undefined, // signal
				);
			});

			// Open dropdown to check available options
			await act(async () => {
				fireEvent.mouseDown(selectElement);
			});

			// Wait for dropdown to populate with API data
			await waitFor(() => {
				expect(
					screen.getByRole('option', { name: 'frontend' }),
				).toBeInTheDocument();
			});

			expect(screen.getByRole('option', { name: 'backend' })).toBeInTheDocument();
			expect(screen.getByRole('option', { name: 'database' })).toBeInTheDocument();
			expect(screen.getByRole('option', { name: 'cache' })).toBeInTheDocument();

			// Check if the first value 'frontend' is selected (active)
			const frontendOption = screen.getByRole('option', { name: 'frontend' });
			expect(frontendOption).toHaveClass('active');
		});
	});

	describe('Multi Select Default Values - ALL Enabled', () => {
		it('should use default value when no previous selection exists', async () => {
			const variableData: IDashboardVariable = {
				id: 'var1',
				name: 'services',
				type: 'DYNAMIC',
				multiSelect: true,
				showALLOption: true,
				defaultValue: ['backend', 'database'] as unknown as string,
				selectedValue: undefined,
				dynamicVariablesAttribute: 'service.name',
				dynamicVariablesSource: 'Traces',
				description: '',
				sort: 'DISABLED',
				allSelected: false,
			};

			await act(async () => {
				renderWithQueryClient(
					<DynamicVariableInput
						variableData={variableData}
						existingVariables={{ var1: variableData }}
						onValueUpdate={mockOnValueUpdate}
					/>,
				);
			});

			expect(mockOnValueUpdate).toHaveBeenCalledWith(
				'services',
				'var1',
				['backend', 'database'],
				true,
				true,
			);
		});

		it('should preserve previous selection over default', async () => {
			const variableData: IDashboardVariable = {
				id: 'var1',
				name: 'services',
				type: 'DYNAMIC',
				multiSelect: true,
				showALLOption: true,
				defaultValue: ['backend'] as unknown as string,
				selectedValue: ['frontend', 'cache'],
				dynamicVariablesAttribute: 'service.name',
				dynamicVariablesSource: 'Traces',
				description: '',
				sort: 'DISABLED',
				allSelected: false,
			};

			await act(async () => {
				renderWithQueryClient(
					<DynamicVariableInput
						variableData={variableData}
						existingVariables={{ var1: variableData }}
						onValueUpdate={mockOnValueUpdate}
					/>,
				);
			});

			// Should NOT call onValueUpdate since previous selection exists
			expect(mockOnValueUpdate).not.toHaveBeenCalledWith();

			// The component shows individual selected values ['frontend', 'cache']
			// when specific values are selected (not ALL)
			expect(screen.getByText('frontend')).toBeInTheDocument();
			expect(screen.getByText('cache')).toBeInTheDocument();

			// Verify that the multi-select wrapper is present
			const multiselectWrapper = screen
				.getByTestId('variable-select')
				.closest('.custom-multiselect-wrapper');
			expect(multiselectWrapper).toBeInTheDocument();

			// Verify that 'backend' (default value) is NOT displayed since previous selection takes priority
			expect(screen.queryByText('backend')).not.toBeInTheDocument();
		});

		it('sahould default to ALL when no default and no previous selection', async () => {
			const variableData: IDashboardVariable = {
				id: 'var21',
				name: 'services',
				type: 'DYNAMIC',
				multiSelect: true,
				showALLOption: true,
				defaultValue: undefined,
				selectedValue: undefined,
				dynamicVariablesAttribute: 'service.name',
				dynamicVariablesSource: 'Traces',
				description: '',
				sort: 'DISABLED',
				allSelected: false,
			};

			await act(async () => {
				renderWithQueryClient(
					<DynamicVariableInput
						variableData={variableData}
						existingVariables={{ var1: variableData }}
						onValueUpdate={mockOnValueUpdate}
					/>,
				);
			});

			// Open dropdown to check if ALL option is selected
			const selectElement = screen.getByRole('combobox');
			await act(async () => {
				fireEvent.mouseDown(selectElement);
			});

			// Check if ALL option is present in dropdown
			const allOption = screen.getByText('ALL');
			expect(allOption).toBeInTheDocument();

			// Verify that the ALL option is available for selection
			const allOptionContainer = allOption.closest(
				'.option-item, .ant-select-item-option',
			);
			expect(allOptionContainer).toBeInTheDocument();

			// Check if the checkbox exists (it should be unchecked initially)
			const checkbox = allOptionContainer?.querySelector(
				'input[type="checkbox"]',
			) as HTMLInputElement;
			expect(checkbox).toBeInTheDocument();

			// Should call onValueUpdate with all values (ALL selection)
			expect(mockOnValueUpdate).toHaveBeenCalledWith(
				'services',
				'var21',
				[],
				true, // allSelected = true
				false,
			);
		});
	});

	describe('Multi Select Default Values - ALL Disabled', () => {
		it('should use default value over first value when provided', async () => {
			const variableData: IDashboardVariable = {
				id: 'var1',
				name: 'services',
				type: 'DYNAMIC',
				multiSelect: true,
				showALLOption: false,
				defaultValue: ['database', 'cache'] as unknown as string,
				selectedValue: undefined,
				dynamicVariablesAttribute: 'service.name',
				dynamicVariablesSource: 'Traces',
				description: '',
				sort: 'DISABLED',
				allSelected: false,
			};

			await act(async () => {
				renderWithQueryClient(
					<DynamicVariableInput
						variableData={variableData}
						existingVariables={{ var1: variableData }}
						onValueUpdate={mockOnValueUpdate}
					/>,
				);
			});

			// Should call onValueUpdate with default values
			expect(mockOnValueUpdate).toHaveBeenCalledWith(
				'services',
				'var1',
				['database', 'cache'],
				true,
				true,
			);
		});
	});

	describe('ALL Option Special Value', () => {
		it('should display ALL correctly when all values are selected', async () => {
			const variableData: IDashboardVariable = {
				id: 'var1',
				name: 'services',
				type: 'DYNAMIC',
				multiSelect: true,
				showALLOption: true,
				allSelected: true,
				selectedValue: ['frontend', 'backend', 'database', 'cache'],
				dynamicVariablesAttribute: 'service.name',
				dynamicVariablesSource: 'Traces',
				description: '',
				sort: 'DISABLED',
			};

			await act(async () => {
				renderWithQueryClient(
					<DynamicVariableInput
						variableData={variableData}
						existingVariables={{ var1: variableData }}
						onValueUpdate={mockOnValueUpdate}
					/>,
				);
			});

			// Check if ALL is displayed in the UI (in the main selection area)
			const allTextElement = screen.getByText('ALL');
			expect(allTextElement).toBeInTheDocument();

			// Verify that the ALL selection wrapper is present and has correct class
			const allSelectedWrapper = allTextElement.closest(
				'.custom-multiselect-wrapper',
			);
			expect(allSelectedWrapper).toHaveClass('all-selected');

			// Open dropdown to check if ALL option is selected/active
			const selectElement = screen.getByRole('combobox');
			await act(async () => {
				fireEvent.mouseDown(selectElement);
			});

			// Wait for API data to be loaded and dropdown to populate
			await waitFor(() => {
				expect(getFieldValues).toHaveBeenCalledWith(
					'traces',
					'service.name',
					'',
					'2023-01-01T00:00:00Z',
					'2023-01-02T00:00:00Z',
					'',
					undefined, // signal
				);
			});

			// Check if ALL option is present in dropdown and selected
			// Use getAllByText to get all ALL elements and find the one in dropdown
			const allElements = screen.getAllByText('ALL');
			expect(allElements.length).toBeGreaterThan(1); // Should have ALL in UI and dropdown

			// Find the ALL option in the dropdown (should have the 'all-option' class)
			const dropdownAllOption = screen.getByRole('option', { name: 'ALL' });
			expect(dropdownAllOption).toBeInTheDocument();
			expect(dropdownAllOption).toHaveClass('all-option');
			expect(dropdownAllOption).toHaveClass('selected');

			// Check if the checkbox for ALL option is checked
			const checkbox = dropdownAllOption.querySelector(
				'input[type="checkbox"]',
			) as HTMLInputElement;
			expect(checkbox).toBeInTheDocument();
			expect(checkbox.checked).toBe(true);
		});
	});
});
