import React from 'react';
import { fireEvent, screen, waitFor } from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import {
    AccountingRunActionButton,
    type AccountingRunActionButtonPropTypes,
} from 'src/components/accounting-run-list/accounting-run-action-button';
import {
    ACCOUNTING_PERIOD_STATUSES as PERIOD_STATUSES,
    ACCOUNTING_RUN_ACTIONS as RUN_ACTIONS,
    ACCOUNTING_RUN_STATUSES as RUN_STATUSES,
} from 'src/constants';
import { AccountingRunContext } from 'src/contexts/accounting-run-context';

describe('<AccountingRunActionButton>', () => {
    const render = (
        props: AccountingRunActionButtonPropTypes,
        contextValues: any
    ) =>
        renderInAppContext(
            <AccountingRunContext.Provider value={contextValues}>
                <AccountingRunActionButton {...props} />
            </AccountingRunContext.Provider>
        );
    const { OPEN, LOCKED, CLOSED } = PERIOD_STATUSES;

    const runId = 123;

    describe(`${RUN_ACTIONS.APPROVE} button`, () => {
        const props = {
            accountingRunId: runId,
            action: RUN_ACTIONS.APPROVE,
            isUpdatingRun: false,
            setUpdatingRun: jest.fn(),
        };

        it(`is disabled if acct period status is not ${LOCKED}`, () => {
            const contextValues = {
                accountingRuns: {
                    123: { status: RUN_STATUSES.NO_ACTION_TAKEN },
                },
                updateRunStatus: jest.fn().mockResolvedValue({}),
                accountingPeriodDetail: {
                    accountingPeriodStatus: OPEN,
                },
            };
            const { getByTestId } = render(props, contextValues);
            const approveButton = getByTestId(`approveAccountingRun-${runId}`);
            expect(approveButton).toHaveProperty('disabled');
        });

        it(`is disabled if another run is in a state of ${RUN_STATUSES.APPROVING}`, () => {
            const contextValues = {
                accountingRuns: { 123: { status: RUN_STATUSES.APPROVING } },
                updateRunStatus: jest.fn().mockResolvedValue({}),
                arePeriodActionsComplete: true,
                accountingPeriodDetail: {
                    accountingPeriodStatus: LOCKED,
                },
            };
            const { getByTestId } = render(props, contextValues);
            const approveButton = getByTestId(`approveAccountingRun-${runId}`);
            expect(approveButton).toHaveProperty('disabled');
        });

        it(`is enabled if acct period status is ${LOCKED} and period actions are complete`, () => {
            const contextValues = {
                accountingRuns: { 123: { status: RUN_STATUSES.COMPLETE } },
                updateRunStatus: jest.fn().mockResolvedValue({}),
                arePeriodActionsComplete: true,
                accountingPeriodDetail: {
                    accountingPeriodStatus: LOCKED,
                },
            };
            const { getByTestId } = render(props, contextValues);
            const approveButton = getByTestId(`approveAccountingRun-${runId}`);
            expect(approveButton.disabled).toBeFalsy();
        });

        it('opens a confirmation modal when clicked', async () => {
            const contextValues = {
                accountingRuns: { 123: { status: RUN_STATUSES.COMPLETE } },
                updateRunStatus: jest.fn().mockResolvedValue({}),
                arePeriodActionsComplete: true,
                accountingPeriodDetail: {
                    accountingPeriodStatus: LOCKED,
                },
            };
            render(props, contextValues);

            const approveButton = screen.getByTestId(
                `approveAccountingRun-${runId}`
            );
            expect(approveButton).toBeTruthy();
            fireEvent.click(approveButton);
            await waitFor(() => {
                expect(
                    screen.getByTestId('AccountingRunModalTestId')
                ).toBeDefined();
            });
        });

        it(`updates run status to ${RUN_STATUSES.APPROVING} when confirm button is clicked`, async () => {
            const contextValues = {
                accountingRuns: { 123: { status: RUN_STATUSES.COMPLETE } },
                updateRunStatus: jest.fn().mockResolvedValue({}),
                arePeriodActionsComplete: true,
                accountingPeriodDetail: {
                    accountingPeriodStatus: LOCKED,
                },
            };
            const { getByTestId } = render(props, contextValues);

            const approveButton = getByTestId(`approveAccountingRun-${runId}`);
            fireEvent.click(approveButton);

            const confirmButtons = await screen.findAllByText('Approve');
            fireEvent.click(confirmButtons[1]);

            await waitFor(() => {
                expect(contextValues.updateRunStatus).toHaveBeenCalledWith(
                    runId,
                    RUN_STATUSES.APPROVING
                );
            });
        });
    });

    describe(`${RUN_ACTIONS.CREATE} button`, () => {
        const props = {
            accountingRunId: runId,
            action: RUN_ACTIONS.CREATE,
            isUpdatingRun: false,
            setUpdatingRun: jest.fn(),
        };

        it(`is disabled if acct period status is ${OPEN}`, () => {
            const contextValues = {
                updateRunStatus: jest.fn().mockResolvedValue({}),
                accountingPeriodDetail: {
                    accountingPeriodStatus: OPEN,
                },
            };
            const { getByTestId } = render(props, contextValues);
            const createButton = getByTestId(`createAccountingRun-${runId}`);
            expect(createButton.disabled).toBeTruthy();
        });

        it(`is enabled if acct period status is ${LOCKED} and period actions are complete`, () => {
            const contextValues = {
                updateRunStatus: jest.fn().mockResolvedValue({}),
                arePeriodActionsComplete: true,
                accountingPeriodDetail: {
                    accountingPeriodStatus: LOCKED,
                },
            };
            const { getByTestId } = render(props, contextValues);
            const createButton = getByTestId(`createAccountingRun-${runId}`);
            expect(createButton.disabled).toBeFalsy();
        });

        it('updates the accounting run status when clicked', async () => {
            const contextValues = {
                updateRunStatus: jest.fn().mockResolvedValue({}),
                arePeriodActionsComplete: true,
                accountingPeriodDetail: {
                    accountingPeriodStatus: LOCKED,
                },
            };
            const { getByTestId } = render(props, contextValues);

            const createButton = getByTestId(`createAccountingRun-${runId}`);
            fireEvent.click(createButton);

            await waitFor(() => {
                expect(contextValues.updateRunStatus).toHaveBeenCalledWith(
                    runId,
                    RUN_STATUSES.WAITING_TO_RUN
                );
            });
        });
    });

    describe(`${RUN_ACTIONS.SKIP} button`, () => {
        const props = {
            accountingRunId: runId,
            action: RUN_ACTIONS.SKIP,
            isUpdatingRun: false,
            setUpdatingRun: jest.fn(),
        };

        it(`is disabled if acct period status is not ${LOCKED}`, () => {
            const contextValues = {
                updateRunStatus: jest.fn().mockResolvedValue({}),
                accountingPeriodDetail: {
                    accountingPeriodStatus: OPEN,
                },
            };
            const { getByTestId } = render(props, contextValues);
            const skipButton = getByTestId(`skipAccountingRun-${runId}`);
            expect(skipButton.disabled).toBeTruthy();
        });

        it(`is enabled if acct period status is ${LOCKED} and period actions are complete`, () => {
            const contextValues = {
                updateRunStatus: jest.fn().mockResolvedValue({}),
                arePeriodActionsComplete: true,
                accountingPeriodDetail: {
                    accountingPeriodStatus: LOCKED,
                },
            };
            const { getByTestId } = render(props, contextValues);
            const skipButton = getByTestId(`skipAccountingRun-${runId}`);
            expect(skipButton.disabled).toBeFalsy();
        });

        it('updates the accounting run status when clicked', async () => {
            const contextValues = {
                updateRunStatus: jest.fn().mockResolvedValue({}),
                arePeriodActionsComplete: true,
                accountingPeriodDetail: {
                    accountingPeriodStatus: LOCKED,
                },
            };
            const { getByTestId } = render(props, contextValues);

            const skipButton = getByTestId(`skipAccountingRun-${runId}`);
            fireEvent.click(skipButton);

            await waitFor(() => {
                expect(contextValues.updateRunStatus).toHaveBeenCalledWith(
                    runId,
                    RUN_STATUSES.SKIPPED
                );
            });
        });
    });

    describe(`${RUN_ACTIONS.INVALIDATE} button`, () => {
        const props = {
            accountingRunId: runId,
            action: RUN_ACTIONS.INVALIDATE,
            isUpdatingRun: false,
            setUpdatingRun: jest.fn(),
        };

        it(`updates the run status to ${RUN_STATUSES.INVALID} when clicked`, async () => {
            const contextValues = {
                updateRunStatus: jest.fn().mockResolvedValue({}),
                accountingPeriodDetail: {
                    accountingPeriodStatus: LOCKED,
                },
            };
            const { getByTestId } = render(props, contextValues);

            const invalidateButton = getByTestId(
                `invalidateAccountingRun-${runId}`
            );
            fireEvent.click(invalidateButton);

            await waitFor(() => {
                expect(contextValues.updateRunStatus).toHaveBeenCalledWith(
                    runId,
                    RUN_STATUSES.INVALID
                );
            });
        });
    });

    describe(`${RUN_ACTIONS.UNDO} button`, () => {
        const props = {
            accountingRunId: runId,
            action: RUN_ACTIONS.UNDO,
            isUpdatingRun: false,
            setUpdatingRun: jest.fn(),
        };

        it(`renders disabled if the accounting period status is ${CLOSED}`, () => {
            const contextValues = {
                updateRunStatus: jest.fn().mockResolvedValue({}),
                accountingPeriodDetail: {
                    accountingPeriodStatus: CLOSED,
                },
            };
            const { getByTestId } = render(props, contextValues);
            const undoButton = getByTestId(`undoAccountingRun-${runId}`);

            expect(undoButton.disabled).toBe(true);
        });

        it(`renders enabled if the accounting period status is NOT ${CLOSED}`, () => {
            const contextValues = {
                updateRunStatus: jest.fn().mockResolvedValue({}),
                accountingPeriodDetail: {
                    accountingPeriodStatus: LOCKED,
                },
            };
            const { getByTestId } = render(props, contextValues);
            const undoButton = getByTestId(`undoAccountingRun-${runId}`);

            expect(undoButton.disabled).toBe(false);
        });

        it(`updates the run status to ${RUN_STATUSES.NO_ACTION_TAKEN} when clicked`, async () => {
            const contextValues = {
                updateRunStatus: jest.fn().mockResolvedValue({}),
                accountingPeriodDetail: {
                    accountingPeriodStatus: LOCKED,
                },
            };
            const { getByTestId } = render(props, contextValues);

            const undoButton = getByTestId(`undoAccountingRun-${runId}`);
            fireEvent.click(undoButton);

            await waitFor(() => {
                expect(contextValues.updateRunStatus).toHaveBeenCalledWith(
                    runId,
                    RUN_STATUSES.NO_ACTION_TAKEN
                );
            });
        });
    });
});
