import React from 'react';
import { renderInAppContext } from '@theorchard/suite-testing';
import { directPayments } from 'src/__fixtures__/graphql/direct-payments';
import * as directPaymentsQuery from 'src/apollo/queries/collaborators/dp-payments';
import PaymentManagement from 'src/components/payment-management/payment-management';
import type { CollaboratorsDpPaymentsQuery } from 'src/apollo/queries/collaborators/__generated__/dp-payments';
import { fireEvent, screen } from '@testing-library/react';
import { ApolloError } from '@apollo/client';
import { selectOption, selectStatusOption } from 'lib/test-utils/select';
import { DpPayoneerPaymentStatus } from 'src/apollo/definitions/globalTypes';
import { EMPTY_CHAR } from '@theorchard/accounting-apps-shared';

jest.mock('src/apollo/queries/payees', () => ({
    useGetPayeeCollaboratorContact: jest.fn(() => ({
        contact: {
            firstName: 'Alice',
            lastName: 'Smith',
            email: 'alice@example.com',
        },
        collaborator: { id: '101', name: 'Alice Smith', currentBalance: null },
        loading: false,
    })),
    useGetPayeeDetailsById: jest.fn(() => ({
        payee: undefined,
        bankDetails: null,
        loading: false,
    })),
}));

describe('<DirectPayments>', () => {
    afterEach(jest.restoreAllMocks);

    beforeEach(() => {
        jest.spyOn(
            directPaymentsQuery,
            'useCollaboratorsDpPaymentsQuery'
        ).mockReturnValue({
            data: directPayments as CollaboratorsDpPaymentsQuery,
            error: undefined,
            loading: false,
        } as any);
    });

    const render = () => renderInAppContext(<PaymentManagement />);

    it('renders with correct column headers', () => {
        render();
        const headerText: string[] = [];
        document
            .querySelectorAll('div.GridTable-column-text')
            .forEach((cell: any) => headerText.push(cell.textContent));

        expect(headerText).toStrictEqual([
            'Payment Status',
            'Error Message',
            'Amount',
            'Statement Period & ID',
            'Collaborator Name & ID',
            'Account Name & ID',
            'Program Name & ID',
            'Payment ID',
            'Actions',
        ]);
    });

    it('renders with correct data', () => {
        render();
        const bodyText: string[] = [];
        document
            .querySelectorAll('div.GridTable-cell')
            .forEach((cell: any) => bodyText.push(cell.textContent));

        expect(bodyText).toStrictEqual([
            'Pending',
            EMPTY_CHAR,
            '$1,000.00',
            'Q1 2024sp-1',
            'Alice Smith101',
            'Test Account 1201',
            'Program A101',
            'payment-1',
            '',
            'Pending',
            EMPTY_CHAR,
            '$2,500.50',
            'Q2 2024sp-2',
            'Bob Jones102',
            'Test Account 2202',
            'Program B102',
            'payment-2',
            '',
            'Successful',
            EMPTY_CHAR,
            '€750.25',
            'Q3 2024sp-3',
            'Carol White103',
            'Test Account 3203',
            'Program C103',
            'payment-3',
            '',
            'Cancelled',
            'Payoneer account is not active',
            '£300.00',
            'Q4 2024sp-4',
            'Dan Brown104',
            'Test Account 4204',
            'Program D104',
            'payment-4',
            '',
        ]);
    });

    it('renders the failure reason when a payment has one', () => {
        render();

        expect(
            screen.getByText('Payoneer account is not active')
        ).toBeInTheDocument();
    });

    it('renders the EMPTY_CHAR placeholder when a payment has no failure reason', () => {
        render();

        // payment-1 (null), payment-2 (null) and payment-3 (undefined) have
        // no reason, so the Error Message cell falls back to EMPTY_CHAR.
        expect(screen.getAllByText(EMPTY_CHAR)).toHaveLength(3);
    });

    describe('filters', () => {
        it('filters by payment status', async () => {
            render();

            const statusSelect = screen.getAllByTestId('SuiteSelect')[0];
            const statusInput = statusSelect.querySelector(
                'input[role="combobox"]'
            ) as HTMLInputElement;
            fireEvent.click(statusInput);
            await screen.findByTestId('SuiteListView');

            selectStatusOption({ label: 'Successful' });

            expect(
                directPaymentsQuery.useCollaboratorsDpPaymentsQuery
            ).toHaveBeenLastCalledWith(
                expect.objectContaining({
                    payoneerStatus: DpPayoneerPaymentStatus.COMPLETE,
                })
            );
        });

        it('filters by collaborator', async () => {
            render();

            const collaboratorSelect = screen.getAllByTestId('SuiteSelect')[1];
            const collaboratorInput = collaboratorSelect.querySelector(
                'input[role="combobox"]'
            ) as HTMLInputElement;
            fireEvent.click(collaboratorInput);
            await screen.findByTestId('SuiteListView');

            selectOption({ label: 'Alice Smith' });

            expect(
                directPaymentsQuery.useCollaboratorsDpPaymentsQuery
            ).toHaveBeenLastCalledWith(
                expect.objectContaining({ collaboratorId: 101 })
            );
        });

        it('filters by account', async () => {
            render();

            const accountSelect = screen.getAllByTestId('SuiteSelect')[2];
            const accountInput = accountSelect.querySelector(
                'input[role="combobox"]'
            ) as HTMLInputElement;
            fireEvent.click(accountInput);
            await screen.findByTestId('SuiteListView');

            selectOption({ label: 'Test Account 1' });

            expect(
                directPaymentsQuery.useCollaboratorsDpPaymentsQuery
            ).toHaveBeenLastCalledWith(
                expect.objectContaining({ accountId: 201 })
            );
        });

        it('filters by statement period', async () => {
            render();

            const statementPeriodSelect =
                screen.getAllByTestId('SuiteSelect')[3];
            const statementPeriodInput = statementPeriodSelect.querySelector(
                'input[role="combobox"]'
            ) as HTMLInputElement;
            fireEvent.click(statementPeriodInput);
            await screen.findByTestId('SuiteListView');

            selectOption({ label: 'Q1 2024' });

            expect(
                directPaymentsQuery.useCollaboratorsDpPaymentsQuery
            ).toHaveBeenLastCalledWith(
                expect.objectContaining({ abacusStatementPeriodId: 'sp-1' })
            );
        });
    });

    describe('sorting', () => {
        it.each([
            { name: 'payoneerStatus', expectedSortKey: 'payoneer_status' },
            { name: 'amount', expectedSortKey: 'amount' },
            { name: 'collaboratorName', expectedSortKey: 'collaborator_name' },
            { name: 'accountName', expectedSortKey: 'account_name' },
            {
                name: 'payoneerProgramName',
                expectedSortKey: 'payoneer_program_name',
            },
        ])('sorts by $name column', ({ name, expectedSortKey }) => {
            render();

            fireEvent.click(document.querySelector(`.col-${name}`)!);

            expect(
                directPaymentsQuery.useCollaboratorsDpPaymentsQuery
            ).toHaveBeenLastCalledWith(
                expect.objectContaining({
                    sortKey: expectedSortKey,
                    sortDirection: 'ASC',
                })
            );
        });
    });

    it('opens payee details modal when Actions button is clicked', async () => {
        render();

        const moreDetailsButton = screen
            .getAllByRole('button')
            .find(button =>
                button.querySelector('[data-testid="MoreDetailsGlyphIcon"]')
            );
        expect(moreDetailsButton).toBeDefined();

        fireEvent.click(moreDetailsButton!);

        expect(
            await screen.findByText('Collaborator Payee')
        ).toBeInTheDocument();
        expect(
            await screen.findByText('Collaborator Details')
        ).toBeInTheDocument();
        expect(screen.getByText('Contact Details')).toBeInTheDocument();
        expect(screen.getAllByText('Alice Smith').length).toBeGreaterThan(0);
        expect(screen.getByText('alice@example.com')).toBeInTheDocument();

        fireEvent.click(screen.getByRole('button', { name: 'Cancel' }));
    });

    it('renders error message on error', () => {
        jest.spyOn(
            directPaymentsQuery,
            'useCollaboratorsDpPaymentsQuery'
        ).mockReturnValue({
            data: undefined,
            error: new ApolloError({
                errorMessage: 'Failed to fetch data',
                graphQLErrors: [],
                clientErrors: [],
                networkError: null,
            }),
            loading: false,
        } as any);

        render();
        expect(screen.getByText('Failed to fetch data')).toBeDefined();
    });
});
