import React from 'react';
import { fireEvent, screen, waitFor } from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import DragAndDrop from 'src/components/shared/drag-and-drop';
import {
    mockBaseDistroCondition,
    mockNewDistroCondition,
} from 'src/__fixtures__/graphql/contract-term-conditions';
import {
    mockUseStoreList,
    mockUseTransactionTypes,
    mockUseTransactionTypeGroups,
} from 'src/__fixtures__/graphql/contract-term-hooks';
import { ContractTermConditionForm } from 'src/components/contract-terms-refactored/contract-terms-condition-form';

jest.mock('src/components/shared/service-multiselect', () => ({
    ServiceMultiSelect: () => <div data-testid="ServiceMultiSelect" />,
}));
jest.mock('src/components/shared/transaction-type-multiselect', () => ({
    TransactionTypeMultiSelect: () => (
        <div data-testid="TransactionTypeMultiSelect" />
    ),
}));
jest.mock('@theorchard/suite-components', () => ({
    ...jest.requireActual('@theorchard/suite-components'),
    MarketSelector: () => <div data-testid="SuiteMarketSelector" />,
}));

describe('<ContractTermConditionForm />', () => {
    const defaultProps = {
        condition: mockBaseDistroCondition,
        index: 0,
        dragAndDropCondition: jest.fn(),
        isDraggable: true,
        conditions: [mockBaseDistroCondition],
        onChange: jest.fn(),
        onDelete: jest.fn(),
        setErrors: jest.fn(),
    };

    const render = (props = {}) =>
        renderInAppContext(
            <DragAndDrop items={[{ id: mockBaseDistroCondition.priority }]}>
                <ContractTermConditionForm {...defaultProps} {...props} />
            </DragAndDrop>
        );

    beforeEach(() => {
        jest.clearAllMocks();
        mockUseStoreList();
        mockUseTransactionTypes();
        mockUseTransactionTypeGroups();
    });

    it('renders drag icon and priority', () => {
        render();
        expect(
            screen.getByTestId('contractTermConditionDragIcon')
        ).toBeInTheDocument();
        expect(
            screen.getByTestId('contractTermConditionRow')
        ).toHaveTextContent('1');
    });

    it('renders form controls (mocked)', () => {
        render();
        expect(
            screen.getByTestId('TransactionTypeMultiSelect')
        ).toBeInTheDocument();
        expect(screen.getByTestId('ServiceMultiSelect')).toBeInTheDocument();
        expect(screen.getByTestId('SuiteMarketSelector')).toBeInTheDocument();
    });

    it('calls onChange when typing into termRate', () => {
        const onChange = jest.fn();
        render({ onChange });

        const input = screen.getByTestId('termRate') as HTMLInputElement;
        fireEvent.change(input, { target: { value: '12.3' } });

        expect(onChange).toHaveBeenCalledWith(0, 'termRate', '12.3');
    });

    it('calls setErrors with validation message on invalid input', () => {
        const setErrors = jest.fn();
        render({ setErrors });

        const input = screen.getByTestId('termRate') as HTMLInputElement;
        fireEvent.change(input, { target: { value: 'bad' } });
        fireEvent.blur(input);

        expect(setErrors).toHaveBeenCalledWith(expect.any(Function));
    });

    it('calls onDelete when delete icon is clicked', async () => {
        const onDelete = jest.fn();
        const deletableCondition = mockNewDistroCondition;
        render({
            onDelete,
            condition: deletableCondition,
            conditions: [deletableCondition],
        });

        const deleteIcon = screen.getByTestId(
            'contractTermConditionDeleteIcon'
        );
        fireEvent.click(deleteIcon);

        await waitFor(() => {
            expect(onDelete).toHaveBeenCalledWith(deletableCondition, 0);
        });
    });

    it('prevents delete when only one condition with valid ID', () => {
        const onDelete = jest.fn();
        render({ onDelete });

        const deleteIcon = screen.getByTestId(
            'contractTermConditionDeleteIcon'
        );
        expect(deleteIcon).toHaveClass('disableDelete');
        fireEvent.click(deleteIcon);
        expect(onDelete).not.toHaveBeenCalled();
    });

    it('allows delete when only condition has null ID', () => {
        const onDelete = jest.fn();
        render({
            onDelete,
            condition: mockNewDistroCondition,
            conditions: [mockNewDistroCondition],
        });

        const deleteIcon = screen.getByTestId(
            'contractTermConditionDeleteIcon'
        );
        expect(deleteIcon).not.toHaveClass('disableDelete');
        fireEvent.click(deleteIcon);
        expect(onDelete).toHaveBeenCalled();
    });

    it('handles pasting into termRate input and sanitizes it', () => {
        const onChange = jest.fn();
        render({ onChange });

        const input = screen.getByTestId('termRate') as HTMLInputElement;

        fireEvent.paste(input, {
            clipboardData: { getData: () => '1.2' },
        } as unknown as React.ClipboardEvent<HTMLInputElement>);

        expect(onChange).toHaveBeenCalledWith(0, 'termRate', '1.2');
    });

    it('clears error when valid termRate value is entered', () => {
        const setErrors = jest.fn();
        render({ setErrors });

        const input = screen.getByTestId('termRate') as HTMLInputElement;
        fireEvent.change(input, { target: { value: '5.55' } });
        fireEvent.blur(input);

        expect(setErrors).toHaveBeenCalledWith(expect.any(Function));
    });

    it('does not apply drag/drop logic if isDraggable is false', () => {
        render({ isDraggable: false });
        const dragIcon = screen.getByTestId('contractTermConditionDragIcon');
        expect(dragIcon.querySelector('svg')).not.toBeInTheDocument();
    });
});
