import React from 'react';
import { fireEvent, screen, waitFor } from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import { worksheetPaymentContractAdvanceResponse } from 'src/__fixtures__/graphql/worksheet-payment-contract-advance-response';
import * as contractAdvanceMutations from 'src/apollo/mutations/contract-advance';
import * as payoneerOutageAlert from 'src/components/shared/payoneer-service-outage-alert';
import {
    ADVANCE_PAYMENT_ACTIONS,
    PAYONEER_OUTAGE_MESSAGE_TITLE,
    PAYONEER_OUTAGE_MESSAGE_CONTENT,
    PAYONEER_OUTAGE_TYPES,
} from 'src/constants';
import { IS_RESENT_PAYMENT_ALLOWED_SERVER_MSG } from 'src/utils/payment-overview';
import AdvancePaymentApprovalSidecar, {
    AdvancePaymentApprovalSidecarProps,
} from '../advance-payment-approval-sidecar';
import { ABACUS_ACTION_STATUSES } from '@theorchard/accounting-apps-shared';

describe('AdvancePaymentApprovalSidecar', () => {
    const render = (props: AdvancePaymentApprovalSidecarProps) =>
        renderInAppContext(<AdvancePaymentApprovalSidecar {...props} />);

    const props = {
        createdBy: 'abc-cde-12345-9a7a',
        isFeatureAbacusCreateAndApprovePaymentsEnabled: true,
        isFeatureAbacusPayoneerOutageCheckEnabled: true,
        worksheetPaymentContractAdvance:
            worksheetPaymentContractAdvanceResponse.worksheetPaymentContractAdvance as AdvancePaymentApprovalSidecarProps['worksheetPaymentContractAdvance'],
    };

    const sendPayment = jest.fn().mockResolvedValue({});
    let mockUsePayoneerOutageStatus: jest.SpyInstance;

    afterEach(jest.restoreAllMocks);
    beforeEach(() => {
        jest.spyOn(
            contractAdvanceMutations,
            'useSendAdvancePayment'
        ).mockReturnValue({ sendPayment, loading: false });

        // Default mock for usePayoneerOutageStatus (no alert, not outage)
        mockUsePayoneerOutageStatus = jest
            .spyOn(payoneerOutageAlert, 'usePayoneerOutageStatus')
            .mockReturnValue({
                alert: null,
                isPayoneerOutage: false,
                isLoading: false,
                error: undefined,
            });
    });

    it('renders a Sidecar on clicking the “payment approval” button', async () => {
        render(props);

        const paymentApprovalButton = screen.getByRole('button', {
            name: /Payment Approval/i,
        });
        expect(paymentApprovalButton).toBeDefined();
        fireEvent.click(paymentApprovalButton);

        await waitFor(() => {
            expect(
                screen.getByTestId('AdvancePaymentApprovalSidecar')
            ).toBeDefined();
        });

        expect(screen.getByText('For Contract Admin')).toBeDefined();
        expect(screen.getByText('For Finance Admin')).toBeDefined();
    });

    it('disables the Finish button if the user is the same one who created the original contract_advance', async () => {
        render({ ...props, createdBy: 'ed8474f8-8cd9-4fa8-ba72-5c91ba00570f' });

        const paymentApprovalButton = screen.getByRole('button', {
            name: /Payment Approval/i,
        });

        fireEvent.click(paymentApprovalButton);

        await waitFor(() => {
            expect(
                screen.getByRole('button', {
                    name: /Finish/i,
                })
            ).toBeDisabled();
        });
    });

    it('disables the Send to Payoneer button by default', async () => {
        render(props);

        const paymentApprovalButton = screen.getByRole('button', {
            name: /Payment Approval/i,
        });
        expect(paymentApprovalButton).toBeDefined();
        fireEvent.click(paymentApprovalButton);

        const confirmButton = screen.getByRole('button', {
            name: /Send to Payoneer/i,
        });
        expect(confirmButton).toBeDisabled();
    });

    it('disables the Send to Payoneer button when AbacusEvent is send_payments', async () => {
        render({
            ...props,
            worksheetPaymentContractAdvance: {
                ...worksheetPaymentContractAdvanceResponse.worksheetPaymentContractAdvance,
                abacusEvents: [
                    {
                        eventName: ADVANCE_PAYMENT_ACTIONS.SEND_PAYMENTS,
                        eventDate: '2024-04-22 14:51:56.126164',
                    },
                ],
                actionStates: [
                    {
                        abacusStateId: '999002',
                        actionName: ADVANCE_PAYMENT_ACTIONS.FUNDS_CONFIRMED,
                        actionStatus: 'complete',
                        lastModified: ADVANCE_PAYMENT_ACTIONS.SEND_PAYMENTS,
                        lastModifiedByIdentity: {
                            name: 'Last Modified By Identity Name',
                        },
                        message: null,
                    },
                ],
            },
        });

        const paymentApprovalButton = screen.getByRole('button', {
            name: /Payment Approval/i,
        });
        expect(paymentApprovalButton).toBeDefined();
        fireEvent.click(paymentApprovalButton);

        const confirmButton = screen.getByRole('button', {
            name: /Send to Payoneer/i,
        });
        expect(confirmButton).toBeDisabled();
    });

    it(`enables the Send to Payoneer button when send_payments status is error and the server message is "${IS_RESENT_PAYMENT_ALLOWED_SERVER_MSG}"`, async () => {
        render({
            ...props,
            worksheetPaymentContractAdvance: {
                ...worksheetPaymentContractAdvanceResponse.worksheetPaymentContractAdvance,
                abacusEvents: [
                    {
                        eventName: ADVANCE_PAYMENT_ACTIONS.SEND_PAYMENTS,
                        eventDate: '2024-04-22 14:51:56.126164',
                    },
                ],
                actionStates: [
                    {
                        abacusStateId: '999002',
                        actionName: ADVANCE_PAYMENT_ACTIONS.SEND_PAYMENTS,
                        actionStatus: 'error',
                        lastModified: '2024-04-22 14:51:56.126164',
                        lastModifiedByIdentity: {
                            name: 'Last Modified By Identity Name',
                        },
                        message: IS_RESENT_PAYMENT_ALLOWED_SERVER_MSG,
                    },
                ],
            },
        });
        const paymentApprovalButton = screen.getByRole('button', {
            name: /Payment Approval/i,
        });
        expect(paymentApprovalButton).toBeDefined();
        fireEvent.click(paymentApprovalButton);

        const confirmButton = screen.getByRole('button', {
            name: /Send to Payoneer/i,
        });
        expect(confirmButton).not.toBeDisabled();
    });

    it('enables the Send to Payoneer button and calls the mutation on click', async () => {
        render({
            ...props,
            worksheetPaymentContractAdvance: {
                ...worksheetPaymentContractAdvanceResponse.worksheetPaymentContractAdvance,
                actionStates: [
                    {
                        abacusStateId: '999002',
                        actionName: ADVANCE_PAYMENT_ACTIONS.FUNDS_CONFIRMED,
                        actionStatus: 'complete',
                        lastModified: ADVANCE_PAYMENT_ACTIONS.SEND_PAYMENTS,
                        lastModifiedByIdentity: {
                            name: 'Last Modified By Identity Name',
                        },
                        message: null,
                    },
                ],
            },
        });

        const paymentApprovalButton = screen.getByRole('button', {
            name: /Payment Approval/i,
        });
        expect(paymentApprovalButton).toBeDefined();
        fireEvent.click(paymentApprovalButton);

        const confirmButton = screen.getByRole('button', {
            name: /Send to Payoneer/i,
        });
        expect(confirmButton).not.toBeDisabled();
        fireEvent.click(confirmButton);
        const worksheetPaymentContractAdvanceId =
            worksheetPaymentContractAdvanceResponse
                .worksheetPaymentContractAdvance
                .worksheetPaymentContractAdvanceId;
        expect(sendPayment).toHaveBeenCalledWith({
            variables: {
                worksheetPaymentContractAdvanceId:
                    worksheetPaymentContractAdvanceId,
            },
        });
    });

    it('disables the Send to Payoneer button if FF is disabled', async () => {
        render({
            ...props,
            isFeatureAbacusCreateAndApprovePaymentsEnabled: false,
            worksheetPaymentContractAdvance: {
                ...worksheetPaymentContractAdvanceResponse.worksheetPaymentContractAdvance,
                actionStates: [
                    {
                        abacusStateId: '999002',
                        actionName: ADVANCE_PAYMENT_ACTIONS.FUNDS_CONFIRMED,
                        actionStatus: 'complete',
                        lastModified: ADVANCE_PAYMENT_ACTIONS.SEND_PAYMENTS,
                        lastModifiedByIdentity: {
                            name: 'Last Modified By Identity Name',
                        },
                        message: null,
                    },
                ],
            },
        });

        const paymentApprovalButton = screen.getByRole('button', {
            name: /Payment Approval/i,
        });
        expect(paymentApprovalButton).toBeDefined();
        fireEvent.click(paymentApprovalButton);

        const sendToPayoneerButton = screen.getByRole('button', {
            name: /Send to Payoneer/i,
        });
        expect(sendToPayoneerButton).toBeDisabled();
    });

    describe('Payoneer Service Status Integration Tests', () => {
        // Helper functions for cleaner test setup
        const createMockAlert = (type: string) => (
            <div role="alert" data-variant="warn">
                <div>{PAYONEER_OUTAGE_MESSAGE_TITLE[type]}</div>
                <div>{PAYONEER_OUTAGE_MESSAGE_CONTENT[type]}</div>
            </div>
        );

        const mockPayoneerStatus = (alert: any, isOutage = false) => {
            mockUsePayoneerOutageStatus.mockImplementation(
                ({ skip = false } = {}) => {
                    if (skip) {
                        return { alert: null, isPayoneerOutage: false };
                    }
                    return {
                        alert,
                        isPayoneerOutage: isOutage,
                    };
                }
            );
        };

        // Optimized helper to create active props (salesforceIdConfirmed && fundsConfirmed)
        const createActiveProps = (overrides = {}) => ({
            ...props,
            worksheetPaymentContractAdvance: {
                ...props.worksheetPaymentContractAdvance!,
                actionStates: [
                    {
                        __typename: 'AbacusState' as const,
                        abacusStateId: 'salesforce-confirmed-id',
                        actionName:
                            ADVANCE_PAYMENT_ACTIONS.SALESFORCE_ID_CONFIRMED,
                        actionStatus: ABACUS_ACTION_STATUSES.COMPLETE,
                        lastModified: '2024-01-01T00:00:00Z',
                        message: null,
                        lastModifiedByIdentity: null,
                    },
                    {
                        __typename: 'AbacusState' as const,
                        abacusStateId: 'funds-confirmed-id',
                        actionName: ADVANCE_PAYMENT_ACTIONS.FUNDS_CONFIRMED,
                        actionStatus: ABACUS_ACTION_STATUSES.COMPLETE,
                        lastModified: '2024-01-01T00:00:00Z',
                        message: null,
                        lastModifiedByIdentity: null,
                    },
                ],
            },
            ...overrides,
        });

        // Consolidated helper to open sidecar and wait for it to be ready
        const openSidecar = async (
            testProps?: AdvancePaymentApprovalSidecarProps
        ) => {
            const propsToUse = testProps || createActiveProps();
            render(propsToUse);
            const paymentApprovalButton = screen.getByRole('button', {
                name: /Payment Approval/i,
            });
            fireEvent.click(paymentApprovalButton);

            await waitFor(() => {
                expect(
                    screen.getByTestId('AdvancePaymentApprovalSidecar')
                ).toBeDefined();
            });
        };

        it('handles healthy service correctly when component is active', async () => {
            mockPayoneerStatus(null, false);
            await openSidecar();

            // No alert should be displayed
            expect(screen.queryByRole('alert')).toBeNull();

            // Send To Payoneer button should be enabled
            const sendToPayoneerButton = screen.getByRole('button', {
                name: /Send to Payoneer/i,
            });
            expect(sendToPayoneerButton).not.toBeDisabled();
        });

        it('handles service outage correctly when component is active', async () => {
            const alert = createMockAlert(PAYONEER_OUTAGE_TYPES.OUTAGE);
            mockPayoneerStatus(alert, true);
            await openSidecar();

            // Alert should be displayed with correct content
            expect(screen.getByRole('alert')).toBeInTheDocument();
            expect(
                screen.getByText(
                    PAYONEER_OUTAGE_MESSAGE_TITLE[PAYONEER_OUTAGE_TYPES.OUTAGE]
                )
            ).toBeInTheDocument();
            expect(
                screen.getByText(
                    PAYONEER_OUTAGE_MESSAGE_CONTENT[
                        PAYONEER_OUTAGE_TYPES.OUTAGE
                    ]
                )
            ).toBeInTheDocument();

            // Send To Payoneer button should be disabled
            const sendToPayoneerButton = screen.getByRole('button', {
                name: /Send to Payoneer/i,
            });
            expect(sendToPayoneerButton).toBeDisabled();
        });

        it('handles unknown service status correctly when component is active', async () => {
            const alert = createMockAlert(PAYONEER_OUTAGE_TYPES.UNKNOWN);
            mockPayoneerStatus(alert, false);
            await openSidecar();

            // Alert should be displayed with correct content
            expect(screen.getByRole('alert')).toBeInTheDocument();
            expect(
                screen.getByText(
                    PAYONEER_OUTAGE_MESSAGE_TITLE[PAYONEER_OUTAGE_TYPES.UNKNOWN]
                )
            ).toBeInTheDocument();
            expect(
                screen.getByText(
                    PAYONEER_OUTAGE_MESSAGE_CONTENT[
                        PAYONEER_OUTAGE_TYPES.UNKNOWN
                    ]
                )
            ).toBeInTheDocument();

            // Send To Payoneer button should be enabled (unknown status doesn't disable)
            const sendToPayoneerButton = screen.getByRole('button', {
                name: /Send to Payoneer/i,
            });
            expect(sendToPayoneerButton).not.toBeDisabled();
        });

        it('does not display alert when component is inactive', async () => {
            const alert = createMockAlert(PAYONEER_OUTAGE_TYPES.OUTAGE);
            mockPayoneerStatus(alert, true);

            // Use original props where salesforceIdConfirmed and fundsConfirmed are false
            await openSidecar(props);

            // No alert should be displayed when component is inactive
            expect(screen.queryByRole('alert')).toBeNull();
        });

        it('does not display alert when feature flag is disabled', async () => {
            const alert = createMockAlert(PAYONEER_OUTAGE_TYPES.OUTAGE);
            mockPayoneerStatus(alert, true);

            // Create props with feature flag disabled
            const propsWithDisabledFlag = createActiveProps({
                isFeatureAbacusPayoneerOutageCheckEnabled: false,
            });
            await openSidecar(propsWithDisabledFlag);

            // No alert should be displayed when feature flag is disabled
            expect(screen.queryByRole('alert')).toBeNull();

            // Send To Payoneer button should NOT be disabled by outage since feature is disabled
            const sendToPayoneerButton = screen.getByRole('button', {
                name: /Send to Payoneer/i,
            });
            expect(sendToPayoneerButton).not.toBeDisabled();
        });
    });
});
