import React from 'react';
import { fireEvent, screen, waitFor } from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import * as moneyhubDbtRefreshMutation from 'src/apollo/mutations/moneyhubDbtRefresh';
import * as statementPeriodMutations from 'src/apollo/mutations/statement-periods';
import {
    CLOSE_BALANCES_ACTIONS_TOOLTIP_MSG,
    CLOSE_STATEMENT_PERIOD_TOOLTIP_MSG,
    STATEMENT_PERIOD_ACTIONS,
} from 'src/constants';
import { ActionState } from 'src/types/action-state';
import CloseStatementPeriodButton from '../close-statement-period-button';
import { ABACUS_ACTION_STATUSES } from '@theorchard/accounting-apps-shared';

interface PropTypes {
    areCloseBalancesActionsComplete: boolean;
    previousPeriodActionState?: ActionState | null;
    isPeriodClosed: boolean;
    exchangeRatesAreUploaded: boolean;
    physicalReservesAreReleased: boolean;
    adjustmentsAreApplied: boolean;
    customerAccountingActionsCompleted: boolean;
    isFeatureRemoveApplyAdjustmentsEnabled?: boolean;
    setError: () => void;
}

describe('<CloseStatementPeriodButton />', () => {
    let useUpdateAbacusStateSpy: any;

    const mockActionState = {
        abacusStateId: '1',
        actionName: STATEMENT_PERIOD_ACTIONS.MARK_ADJUSTMENTS_APPLIED,
        actionStatus: ABACUS_ACTION_STATUSES.INIT,
        message: null,
    };

    const closeStatementPeriod = jest.fn().mockResolvedValue({});
    const useMoneyhubDbtRefresh = jest.fn().mockResolvedValue({});

    const render = (props: PropTypes) =>
        renderInAppContext(<CloseStatementPeriodButton {...props} />);

    beforeEach(() => {
        useUpdateAbacusStateSpy = jest
            .spyOn(statementPeriodMutations, 'useCloseStatementPeriod')
            .mockReturnValue(closeStatementPeriod);
        jest.spyOn(
            moneyhubDbtRefreshMutation,
            'useMoneyhubDbtRefresh'
        ).mockReturnValue(useMoneyhubDbtRefresh);
    });

    it('gets the updateState callback on render', () => {
        render({
            areCloseBalancesActionsComplete: false,
            isPeriodClosed: false,
            exchangeRatesAreUploaded: false,
            physicalReservesAreReleased: false,
            adjustmentsAreApplied: false,
            customerAccountingActionsCompleted: false,
            setError: () => jest.fn(),
        });
        expect(useUpdateAbacusStateSpy).toHaveBeenCalled();
    });

    it('renders disabled button if other actions are not complete', () => {
        render({
            areCloseBalancesActionsComplete: false,
            isPeriodClosed: false,
            exchangeRatesAreUploaded: false,
            physicalReservesAreReleased: false,
            adjustmentsAreApplied: false,
            customerAccountingActionsCompleted: false,
            setError: () => jest.fn(),
        });

        const button = screen.getByText('Close Statement Period');
        expect(button).toBeDefined();
        expect(button).toHaveAttribute('disabled');
    });

    it('renders enabled button if all actions are complete', () => {
        render({
            areCloseBalancesActionsComplete: true,
            isPeriodClosed: false,
            exchangeRatesAreUploaded: true,
            physicalReservesAreReleased: true,
            adjustmentsAreApplied: true,
            customerAccountingActionsCompleted: true,
            setError: () => jest.fn(),
        });

        const button = screen.getByText('Close Statement Period');
        expect(button).toBeDefined();
        expect(button.getAttribute('disabled')).toBe(null);
    });

    it('does not render modal before button is clicked', () => {
        render({
            areCloseBalancesActionsComplete: false,
            isPeriodClosed: false,
            exchangeRatesAreUploaded: false,
            physicalReservesAreReleased: false,
            adjustmentsAreApplied: false,
            customerAccountingActionsCompleted: false,
            setError: () => jest.fn(),
        });

        const button = screen.getByText('Close Statement Period');
        expect(button).toBeDefined();
        expect(screen.queryByText('Yes, Close This Period')).toBeNull();
    });

    it('renders modal after button is clicked', () => {
        render({
            areCloseBalancesActionsComplete: true,
            isPeriodClosed: false,
            exchangeRatesAreUploaded: true,
            physicalReservesAreReleased: true,
            adjustmentsAreApplied: true,
            customerAccountingActionsCompleted: true,
            setError: () => jest.fn(),
        });

        const button = screen.getByText('Close Statement Period');
        expect(button).toBeDefined();
        fireEvent.click(button);
        expect(screen.getByText('Yes, Close This Period')).toBeDefined();
    });

    it('calls closeStatementPeriod and dbt refresh after clicking the confirm button on the modal', () => {
        render({
            areCloseBalancesActionsComplete: true,
            isPeriodClosed: false,
            exchangeRatesAreUploaded: true,
            physicalReservesAreReleased: true,
            adjustmentsAreApplied: true,
            customerAccountingActionsCompleted: true,
            setError: () => jest.fn(),
        });

        const button = screen.getByText('Close Statement Period');
        expect(button).toBeDefined();
        fireEvent.click(button);

        const confirmButton = screen.getByRole('button', {
            name: 'Yes, Close This Period',
        });
        fireEvent.click(confirmButton);
        expect(closeStatementPeriod).toHaveBeenCalled();
        expect(useMoneyhubDbtRefresh).toHaveBeenCalled();
        expect(confirmButton).toHaveAttribute('disabled');
    });

    it('enables confirm button when closeStatementPeriod throws an error', async () => {
        const closeStatementPeriodError = jest
            .fn()
            .mockRejectedValue('Error while closing statement period');
        jest.spyOn(
            statementPeriodMutations,
            'useCloseStatementPeriod'
        ).mockReturnValue(closeStatementPeriodError);
        render({
            areCloseBalancesActionsComplete: true,
            isPeriodClosed: false,
            exchangeRatesAreUploaded: true,
            physicalReservesAreReleased: true,
            adjustmentsAreApplied: true,
            customerAccountingActionsCompleted: true,
            setError: () => jest.fn(),
        });

        const button = screen.getByText('Close Statement Period');
        expect(button).toBeDefined();
        fireEvent.click(button);

        const confirmButton = screen.getByRole('button', {
            name: 'Yes, Close This Period',
        });
        fireEvent.click(confirmButton);

        expect(closeStatementPeriodError).toHaveBeenCalled();
        await waitFor(() =>
            expect(confirmButton).not.toHaveAttribute('disabled')
        );
    });

    it('renders correct text if statement period is closed', () => {
        render({
            areCloseBalancesActionsComplete: true,
            previousPeriodActionState: {
                ...mockActionState,
                abacusStateId: '2',
                actionStatus: ABACUS_ACTION_STATUSES.COMPLETE,
            },
            isPeriodClosed: true,
            exchangeRatesAreUploaded: true,
            physicalReservesAreReleased: true,
            adjustmentsAreApplied: true,
            customerAccountingActionsCompleted: true,
            setError: () => jest.fn(),
        });

        const button = screen.getByText('Statement Period Closed');
        expect(button).toBeDefined();
    });

    it('renders disabled button if customer accounting actions are not complete', () => {
        render({
            areCloseBalancesActionsComplete: true,
            isPeriodClosed: false,
            exchangeRatesAreUploaded: true,
            physicalReservesAreReleased: true,
            adjustmentsAreApplied: true,
            customerAccountingActionsCompleted: false,
            setError: () => jest.fn(),
        });

        const button = screen.getByText('Close Statement Period');
        expect(button).toHaveAttribute('disabled');
    });

    it('renders disabled button if close balances actions are not complete', () => {
        render({
            areCloseBalancesActionsComplete: false,
            isPeriodClosed: false,
            exchangeRatesAreUploaded: true,
            physicalReservesAreReleased: true,
            adjustmentsAreApplied: true,
            customerAccountingActionsCompleted: true,
            setError: () => jest.fn(),
        });

        const button = screen.getByText('Close Statement Period');
        expect(button).toHaveAttribute('disabled');
    });

    it(`renders tooltip when adjustments are not applied`, async () => {
        render({
            areCloseBalancesActionsComplete: false,
            isPeriodClosed: false,
            exchangeRatesAreUploaded: true,
            physicalReservesAreReleased: true,
            adjustmentsAreApplied: false,
            customerAccountingActionsCompleted: true,
            isFeatureRemoveApplyAdjustmentsEnabled: true,
            setError: () => jest.fn(),
        });

        const tooltipIcon = screen.getByTestId(
            'CloseStatementPeriodHelpTooltip'
        );
        expect(tooltipIcon).toBeDefined();
        fireEvent.mouseOver(tooltipIcon);

        const tooltipText = await screen.findByText(
            CLOSE_STATEMENT_PERIOD_TOOLTIP_MSG
        );
        expect(tooltipText).toBeDefined();
    });

    it('renders tooltip when CloseBalancesActions are not completed', async () => {
        render({
            areCloseBalancesActionsComplete: false,
            isPeriodClosed: false,
            exchangeRatesAreUploaded: true,
            physicalReservesAreReleased: true,
            adjustmentsAreApplied: true,
            customerAccountingActionsCompleted: true,
            isFeatureRemoveApplyAdjustmentsEnabled: true,
            setError: () => jest.fn(),
        });

        const tooltipIcon = screen.getByTestId(
            'CloseBalancesActionsHelpTooltip'
        );
        expect(tooltipIcon).toBeDefined();
        fireEvent.mouseOver(tooltipIcon);

        const tooltipText = await screen.findByText(
            CLOSE_BALANCES_ACTIONS_TOOLTIP_MSG
        );
        expect(tooltipText).toBeDefined();
    });

    it(`hides tooltip when close balances actions are completed`, async () => {
        render({
            areCloseBalancesActionsComplete: true,
            isPeriodClosed: false,
            exchangeRatesAreUploaded: true,
            physicalReservesAreReleased: true,
            adjustmentsAreApplied: false,
            customerAccountingActionsCompleted: true,
            isFeatureRemoveApplyAdjustmentsEnabled: false,
            setError: () => jest.fn(),
        });

        const tooltipIcon = screen.queryByTestId(
            'CloseBalancesActionsHelpTooltip'
        );
        expect(tooltipIcon).toBeNull();
    });
});
