import React from 'react';
import { fireEvent, screen, waitFor } from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import { useUpdateSapProfitCenter } from 'src/apollo/mutations/reference-sap-profit-center';
import { EditableDisplayNameCell } from 'src/components/reference-sap-profit-center-edit/editable-display-name-cell';
import { EditableDisplayNameProvider } from 'src/components/reference-sap-profit-center-edit/editable-display-name-context';

jest.mock('src/apollo/mutations/reference-sap-profit-center', () => ({
    useUpdateSapProfitCenter: jest.fn(),
}));

describe('<EditableDisplayNameCell />', () => {
    const rowData = {
        referenceSapProfitCenterId: 'psc-1',
        displayName: 'Original Name',
    };

    const debouncedRefetch = jest.fn();
    let updateSapProfitCenter: jest.Mock;

    const renderComponent = (data = rowData) =>
        renderInAppContext(
            <EditableDisplayNameProvider
                value={{ debouncedRefetch, page: 0, pageSize: 25 }}
            >
                <EditableDisplayNameCell rowData={data} />
            </EditableDisplayNameProvider>
        );

    const getInput = () => screen.getByRole('textbox') as HTMLInputElement;

    beforeEach(() => {
        jest.clearAllMocks();
        updateSapProfitCenter = jest.fn().mockResolvedValue(undefined);
        (useUpdateSapProfitCenter as jest.Mock).mockReturnValue(
            updateSapProfitCenter
        );
    });

    it('renders the current display name in read mode', () => {
        renderComponent();

        expect(screen.getByTestId('read-input')).toHaveTextContent(
            'Original Name'
        );
    });

    it('enters edit mode with the current value pre-filled when the edit button is clicked', () => {
        renderComponent();

        fireEvent.click(screen.getByTestId('EditableMetadata-edit-button'));

        expect(getInput()).toHaveValue('Original Name');
    });

    it('saves the new value: calls the mutation, refetches, and shows a success indicator', async () => {
        renderComponent();

        fireEvent.click(screen.getByTestId('EditableMetadata-edit-button'));
        fireEvent.change(getInput(), { target: { value: 'New Name' } });
        fireEvent.click(screen.getByTestId('EditableMetadata-save-button'));

        expect(updateSapProfitCenter).toHaveBeenCalledWith({
            variables: {
                referenceSapProfitCenterId: 'psc-1',
                displayName: 'New Name',
            },
        });

        await screen.findByTestId('EditableMetadata-success');

        expect(debouncedRefetch).toHaveBeenCalledWith(0, 25);
        expect(screen.getByTestId('read-input')).toHaveTextContent('New Name');
    });

    it('disables the save button when the value is empty or blank', () => {
        renderComponent();

        fireEvent.click(screen.getByTestId('EditableMetadata-edit-button'));
        fireEvent.change(getInput(), { target: { value: '   ' } });

        expect(
            screen.getByTestId('EditableMetadata-save-button')
        ).toBeDisabled();
    });

    it('shows an error message and reverts to the original value when the mutation fails', async () => {
        updateSapProfitCenter.mockRejectedValueOnce(new Error('save failed'));
        renderComponent();

        fireEvent.click(screen.getByTestId('EditableMetadata-edit-button'));
        fireEvent.change(getInput(), { target: { value: 'Broken Name' } });
        fireEvent.click(screen.getByTestId('EditableMetadata-save-button'));

        await screen.findByText('Failed to save changes.');

        expect(screen.getByTestId('read-input')).toHaveTextContent(
            'Original Name'
        );
        expect(debouncedRefetch).not.toHaveBeenCalled();
    });

    it('clears a previous error message once a save succeeds', async () => {
        updateSapProfitCenter.mockRejectedValueOnce(new Error('save failed'));
        renderComponent();

        fireEvent.click(screen.getByTestId('EditableMetadata-edit-button'));
        fireEvent.change(getInput(), { target: { value: 'Broken Name' } });
        fireEvent.click(screen.getByTestId('EditableMetadata-save-button'));

        await screen.findByText('Failed to save changes.');

        // The processing indicator fades out before the edit button reappears.
        await screen.findByTestId('EditableMetadata-edit-button');

        fireEvent.click(screen.getByTestId('EditableMetadata-edit-button'));
        fireEvent.change(getInput(), { target: { value: 'Fixed Name' } });
        fireEvent.click(screen.getByTestId('EditableMetadata-save-button'));

        await waitFor(() =>
            expect(
                screen.queryByText('Failed to save changes.')
            ).not.toBeInTheDocument()
        );
        expect(screen.getByTestId('read-input')).toHaveTextContent(
            'Fixed Name'
        );
    });

    it('keeps the local value in sync when the underlying row data changes externally', () => {
        const { rerender } = renderComponent();

        rerender(
            <EditableDisplayNameProvider
                value={{ debouncedRefetch, page: 0, pageSize: 25 }}
            >
                <EditableDisplayNameCell
                    rowData={{ ...rowData, displayName: 'Externally Updated' }}
                />
            </EditableDisplayNameProvider>
        );

        expect(screen.getByTestId('read-input')).toHaveTextContent(
            'Externally Updated'
        );
    });
});
