import { fireEvent, render, screen } from '@testing-library/react';

import LabelsInput from '../LabelsInput';
import { LabelsInputProps } from '../types';

// Mock the CloseOutlined icon
jest.mock('@ant-design/icons', () => ({
	CloseOutlined: (): JSX.Element => <span data-testid="close-icon">×</span>,
}));

const mockOnLabelsChange = jest.fn();
const mockValidateLabelsKey = jest.fn().mockReturnValue(null);

const defaultProps: LabelsInputProps = {
	labels: {},
	onLabelsChange: mockOnLabelsChange,
	validateLabelsKey: mockValidateLabelsKey,
};

const ADD_LABELS_TEXT = '+ Add labels';
const ENTER_KEY_PLACEHOLDER = 'Enter key';
const ENTER_VALUE_PLACEHOLDER = 'Enter value';

const CLOSE_ICON_TEST_ID = 'close-icon';
const SEVERITY_HIGH_TEXT = 'severity: high';
const ENVIRONMENT_PRODUCTION_TEXT = 'environment: production';
const SEVERITY_HIGH_KEY_VALUE = 'severity:high';

const renderLabelsInput = (
	props: Partial<LabelsInputProps> = {},
): ReturnType<typeof render> =>
	render(<LabelsInput {...defaultProps} {...props} />);

describe('LabelsInput', () => {
	beforeEach(() => {
		jest.clearAllMocks();
		mockValidateLabelsKey.mockReturnValue(null); // Reset validation to always pass
	});

	describe('Initial Rendering', () => {
		it('renders add button when no labels exist', () => {
			renderLabelsInput();
			expect(screen.getByText(ADD_LABELS_TEXT)).toBeInTheDocument();
			expect(screen.queryByTestId(CLOSE_ICON_TEST_ID)).not.toBeInTheDocument();
		});

		it('renders existing labels when provided', () => {
			const labels = { severity: 'high', environment: 'production' };
			renderLabelsInput({ labels });

			expect(screen.getByText(SEVERITY_HIGH_TEXT)).toBeInTheDocument();
			expect(screen.getByText(ENVIRONMENT_PRODUCTION_TEXT)).toBeInTheDocument();
			expect(screen.getAllByTestId(CLOSE_ICON_TEST_ID)).toHaveLength(2);
		});

		it('does not render existing labels section when no labels', () => {
			renderLabelsInput();
			expect(screen.queryByText(SEVERITY_HIGH_TEXT)).not.toBeInTheDocument();
		});
	});

	describe('Adding Labels', () => {
		it('shows input field when add button is clicked', () => {
			renderLabelsInput();

			fireEvent.click(screen.getByText(ADD_LABELS_TEXT));

			expect(
				screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER),
			).toBeInTheDocument();
			expect(screen.queryByText(ADD_LABELS_TEXT)).not.toBeInTheDocument();
		});

		it('switches from key input to value input on Enter', () => {
			renderLabelsInput();

			fireEvent.click(screen.getByText(ADD_LABELS_TEXT));
			const input = screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER);

			fireEvent.change(input, { target: { value: 'severity' } });
			fireEvent.keyDown(input, { key: 'Enter' });

			expect(
				screen.getByPlaceholderText(ENTER_VALUE_PLACEHOLDER),
			).toBeInTheDocument();
			expect(
				screen.queryByPlaceholderText(ENTER_KEY_PLACEHOLDER),
			).not.toBeInTheDocument();
		});

		it('adds label when both key and value are provided', () => {
			renderLabelsInput();

			fireEvent.click(screen.getByText(ADD_LABELS_TEXT));
			const input = screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER);

			// Enter key
			fireEvent.change(input, { target: { value: 'severity' } });
			fireEvent.keyDown(input, { key: 'Enter' });

			// Enter value
			const valueInput = screen.getByPlaceholderText(ENTER_VALUE_PLACEHOLDER);
			fireEvent.change(valueInput, { target: { value: 'high' } });
			fireEvent.keyDown(valueInput, { key: 'Enter' });

			expect(mockOnLabelsChange).toHaveBeenCalledWith({ severity: 'high' });
		});

		it('does not switch to value input if key is empty', () => {
			renderLabelsInput();

			fireEvent.click(screen.getByText(ADD_LABELS_TEXT));
			const input = screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER);

			fireEvent.keyDown(input, { key: 'Enter' });

			expect(
				screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER),
			).toBeInTheDocument();
			expect(
				screen.queryByPlaceholderText(ENTER_VALUE_PLACEHOLDER),
			).not.toBeInTheDocument();
		});

		it('does not add label if value is empty', () => {
			renderLabelsInput();

			fireEvent.click(screen.getByText(ADD_LABELS_TEXT));
			const input = screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER);

			// Enter key
			fireEvent.change(input, { target: { value: 'severity' } });
			fireEvent.keyDown(input, { key: 'Enter' });

			// Try to add with empty value
			const valueInput = screen.getByPlaceholderText(ENTER_VALUE_PLACEHOLDER);
			fireEvent.keyDown(valueInput, { key: 'Enter' });

			expect(mockOnLabelsChange).not.toHaveBeenCalled();
		});

		it('trims whitespace from key and value', () => {
			renderLabelsInput();

			fireEvent.click(screen.getByText(ADD_LABELS_TEXT));
			const input = screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER);

			// Enter key with whitespace
			fireEvent.change(input, { target: { value: '  severity  ' } });
			fireEvent.keyDown(input, { key: 'Enter' });

			// Enter value with whitespace
			const valueInput = screen.getByPlaceholderText(ENTER_VALUE_PLACEHOLDER);
			fireEvent.change(valueInput, { target: { value: '  high  ' } });
			fireEvent.keyDown(valueInput, { key: 'Enter' });

			expect(mockOnLabelsChange).toHaveBeenCalledWith({ severity: 'high' });
		});

		it('resets input state after adding label', () => {
			renderLabelsInput();

			fireEvent.click(screen.getByText(ADD_LABELS_TEXT));
			const input = screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER);

			// Add a label
			fireEvent.change(input, { target: { value: 'severity' } });
			fireEvent.keyDown(input, { key: 'Enter' });

			const valueInput = screen.getByPlaceholderText(ENTER_VALUE_PLACEHOLDER);
			fireEvent.change(valueInput, { target: { value: 'high' } });
			fireEvent.keyDown(valueInput, { key: 'Enter' });

			// Should be back to key input
			expect(
				screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER),
			).toBeInTheDocument();
			expect(
				screen.queryByPlaceholderText(ENTER_VALUE_PLACEHOLDER),
			).not.toBeInTheDocument();
		});
	});

	describe('Removing Labels', () => {
		it('removes label when close button is clicked', () => {
			const labels = { severity: 'high', environment: 'production' };
			renderLabelsInput({ labels });

			const removeButtons = screen.getAllByTestId(CLOSE_ICON_TEST_ID);
			fireEvent.click(removeButtons[0]);

			expect(mockOnLabelsChange).toHaveBeenCalledWith({
				environment: 'production',
			});
		});

		it('calls onLabelsChange with empty object when last label is removed', () => {
			const labels = { severity: 'high' };
			renderLabelsInput({ labels });

			const removeButton = screen.getByTestId('close-icon');
			fireEvent.click(removeButton);

			expect(mockOnLabelsChange).toHaveBeenCalledWith({});
		});
	});

	describe('Keyboard Interactions', () => {
		it('cancels adding label on Escape key', () => {
			renderLabelsInput();

			fireEvent.click(screen.getByText(ADD_LABELS_TEXT));
			const input = screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER);

			fireEvent.keyDown(input, { key: 'Escape' });

			expect(screen.getByText(ADD_LABELS_TEXT)).toBeInTheDocument();
			expect(
				screen.queryByPlaceholderText(ENTER_KEY_PLACEHOLDER),
			).not.toBeInTheDocument();
		});

		it('cancels adding label on Escape key in value input', () => {
			renderLabelsInput();

			fireEvent.click(screen.getByText(ADD_LABELS_TEXT));
			const input = screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER);

			// Enter key
			fireEvent.change(input, { target: { value: 'severity' } });
			fireEvent.keyDown(input, { key: 'Enter' });

			// Cancel in value input
			const valueInput = screen.getByPlaceholderText(ENTER_VALUE_PLACEHOLDER);
			fireEvent.keyDown(valueInput, { key: 'Escape' });

			expect(screen.getByText(ADD_LABELS_TEXT)).toBeInTheDocument();
			expect(
				screen.queryByPlaceholderText(ENTER_VALUE_PLACEHOLDER),
			).not.toBeInTheDocument();
		});
	});

	describe('Blur Behavior', () => {
		it('closes input immediately when both key and value are empty', () => {
			renderLabelsInput();

			fireEvent.click(screen.getByText(ADD_LABELS_TEXT));
			const input = screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER);

			fireEvent.blur(input);

			// The input should close immediately when both key and value are empty
			expect(screen.getByText(ADD_LABELS_TEXT)).toBeInTheDocument();
			expect(
				screen.queryByPlaceholderText(ENTER_KEY_PLACEHOLDER),
			).not.toBeInTheDocument();
		});

		it('does not close input immediately when key has value', () => {
			jest.useFakeTimers();
			renderLabelsInput();

			fireEvent.click(screen.getByText(ADD_LABELS_TEXT));
			const input = screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER);

			fireEvent.change(input, { target: { value: 'severity' } });
			fireEvent.blur(input);

			jest.advanceTimersByTime(200);

			expect(
				screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER),
			).toBeInTheDocument();
			expect(screen.queryByText(ADD_LABELS_TEXT)).not.toBeInTheDocument();

			jest.useRealTimers();
		});
	});

	describe('Input Change Handling', () => {
		it('updates key input value correctly', () => {
			renderLabelsInput();

			fireEvent.click(screen.getByText(ADD_LABELS_TEXT));
			const input = screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER);

			fireEvent.change(input, { target: { value: 'severity' } });

			expect(input).toHaveValue('severity');
		});

		it('updates value input correctly', () => {
			renderLabelsInput();

			fireEvent.click(screen.getByText(ADD_LABELS_TEXT));
			const input = screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER);

			// Enter key
			fireEvent.change(input, { target: { value: 'severity' } });
			fireEvent.keyDown(input, { key: 'Enter' });

			// Update value
			const valueInput = screen.getByPlaceholderText(ENTER_VALUE_PLACEHOLDER);
			fireEvent.change(valueInput, { target: { value: 'high' } });

			expect(valueInput).toHaveValue('high');
		});
	});

	describe('Edge Cases', () => {
		it('handles multiple labels correctly', () => {
			const labels = {
				severity: 'high',
				environment: 'production',
				service: 'api-gateway',
			};
			renderLabelsInput({ labels });

			expect(screen.getByText(SEVERITY_HIGH_TEXT)).toBeInTheDocument();
			expect(screen.getByText(ENVIRONMENT_PRODUCTION_TEXT)).toBeInTheDocument();
			expect(screen.getByText('service: api-gateway')).toBeInTheDocument();
			expect(screen.getAllByTestId(CLOSE_ICON_TEST_ID)).toHaveLength(3);
		});

		it('handles empty string values', () => {
			const labels = { severity: '' };
			renderLabelsInput({ labels });

			expect(screen.getByText(/severity/)).toBeInTheDocument();
		});

		it('handles special characters in labels', () => {
			const labels = { 'service-name': 'api-gateway-v1' };
			renderLabelsInput({ labels });

			expect(screen.getByText('service-name: api-gateway-v1')).toBeInTheDocument();
		});

		it('maintains focus on input after adding label', () => {
			renderLabelsInput();

			fireEvent.click(screen.getByText(ADD_LABELS_TEXT));
			const input = screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER);

			// Add a label
			fireEvent.change(input, { target: { value: 'severity' } });
			fireEvent.keyDown(input, { key: 'Enter' });

			const valueInput = screen.getByPlaceholderText(ENTER_VALUE_PLACEHOLDER);
			fireEvent.change(valueInput, { target: { value: 'high' } });
			fireEvent.keyDown(valueInput, { key: 'Enter' });

			// Should be focused on new key input
			const newInput = screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER);
			expect(newInput).toHaveFocus();
		});
	});

	describe('Key:Value Format Support', () => {
		it('adds label when key:value format is entered and Enter is pressed', () => {
			renderLabelsInput();

			fireEvent.click(screen.getByText(ADD_LABELS_TEXT));
			const input = screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER);

			// Enter key:value format
			fireEvent.change(input, { target: { value: SEVERITY_HIGH_KEY_VALUE } });
			fireEvent.keyDown(input, { key: 'Enter' });

			expect(mockOnLabelsChange).toHaveBeenCalledWith({ severity: 'high' });
		});

		it('trims whitespace from key and value in key:value format', () => {
			renderLabelsInput();

			fireEvent.click(screen.getByText(ADD_LABELS_TEXT));
			const input = screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER);

			// Enter key:value format with whitespace
			fireEvent.change(input, { target: { value: '  severity  :  high  ' } });
			fireEvent.keyDown(input, { key: 'Enter' });

			expect(mockOnLabelsChange).toHaveBeenCalledWith({ severity: 'high' });
		});

		it('handles values with colons correctly', () => {
			renderLabelsInput();

			fireEvent.click(screen.getByText(ADD_LABELS_TEXT));
			const input = screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER);

			// Enter key:value format where value contains colons
			fireEvent.change(input, {
				target: { value: 'url:https://example.com:8080' },
			});
			fireEvent.keyDown(input, { key: 'Enter' });

			expect(mockOnLabelsChange).toHaveBeenCalledWith({
				url: 'https://example.com:8080',
			});
		});

		it('does not add label if key is empty in key:value format', () => {
			renderLabelsInput();

			fireEvent.click(screen.getByText(ADD_LABELS_TEXT));
			const input = screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER);

			// Enter key:value format with empty key
			fireEvent.change(input, { target: { value: ':high' } });
			fireEvent.keyDown(input, { key: 'Enter' });

			expect(mockOnLabelsChange).not.toHaveBeenCalled();
		});

		it('does not add label if value is empty in key:value format', () => {
			renderLabelsInput();

			fireEvent.click(screen.getByText(ADD_LABELS_TEXT));
			const input = screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER);

			// Enter key:value format with empty value
			fireEvent.change(input, { target: { value: 'severity:' } });
			fireEvent.keyDown(input, { key: 'Enter' });

			expect(mockOnLabelsChange).not.toHaveBeenCalled();
		});

		it('does not add label if only colon is entered', () => {
			renderLabelsInput();

			fireEvent.click(screen.getByText(ADD_LABELS_TEXT));
			const input = screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER);

			// Enter only colon
			fireEvent.change(input, { target: { value: ':' } });
			fireEvent.keyDown(input, { key: 'Enter' });

			expect(mockOnLabelsChange).not.toHaveBeenCalled();
		});

		it('resets input state after adding label with key:value format', () => {
			renderLabelsInput();

			fireEvent.click(screen.getByText(ADD_LABELS_TEXT));
			const input = screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER);

			// Add label with key:value format
			fireEvent.change(input, { target: { value: 'severity:high' } });
			fireEvent.keyDown(input, { key: 'Enter' });

			// Should be back to key input for next label
			expect(
				screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER),
			).toBeInTheDocument();
			expect(
				screen.queryByPlaceholderText(ENTER_VALUE_PLACEHOLDER),
			).not.toBeInTheDocument();
		});

		it('does not auto-save when typing key:value without pressing Enter', () => {
			renderLabelsInput();

			fireEvent.click(screen.getByText(ADD_LABELS_TEXT));
			const input = screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER);

			// Type key:value format but don't press Enter
			fireEvent.change(input, { target: { value: SEVERITY_HIGH_KEY_VALUE } });

			// Should not have called onLabelsChange yet
			expect(mockOnLabelsChange).not.toHaveBeenCalled();
		});

		it('handles multiple key:value entries correctly', () => {
			const { rerender } = renderLabelsInput();

			fireEvent.click(screen.getByText(ADD_LABELS_TEXT));
			const input = screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER);

			// Add first label
			fireEvent.change(input, { target: { value: SEVERITY_HIGH_KEY_VALUE } });
			fireEvent.keyDown(input, { key: 'Enter' });

			// Simulate parent component updating labels
			const firstLabels = { severity: 'high' };
			rerender(
				<LabelsInput
					labels={firstLabels}
					onLabelsChange={mockOnLabelsChange}
					validateLabelsKey={mockValidateLabelsKey}
				/>,
			);

			// Add second label
			const newInput = screen.getByPlaceholderText(ENTER_KEY_PLACEHOLDER);
			fireEvent.change(newInput, { target: { value: 'environment:production' } });
			fireEvent.keyDown(newInput, { key: 'Enter' });

			// Check that we made two calls and the last one includes both labels
			expect(mockOnLabelsChange).toHaveBeenCalledTimes(2);
			expect(mockOnLabelsChange).toHaveBeenNthCalledWith(1, { severity: 'high' });
			expect(mockOnLabelsChange).toHaveBeenNthCalledWith(2, {
				severity: 'high',
				environment: 'production',
			});
		});
	});
});
