import React from 'react';
import * as apollo from '@apollo/client';
import { fireEvent, screen } from '@testing-library/react';
import { createIdentity, renderInAppContext } from '@theorchard/suite-testing';
import { Route } from 'react-router-dom';
import { accountContractLedgerList } from 'src/__fixtures__/graphql/account-ledger';
import * as contractList from 'src/__fixtures__/graphql/contract-list-response.json';
import * as statementPeriods from 'src/__fixtures__/graphql/statement-periods-list-response.json';
import * as ledgerQuery from 'src/apollo/queries/ledger';
import * as statementPeriodQuery from 'src/apollo/queries/statement-periods';
import {
    ACCOUNT_LEDGER_ERROR_MESSAGE,
    NO_ACCOUNT_LEDGER_RESULTS,
} from 'src/apollo/type-constants/account';
import {
    AccountLedger,
    AccountLedgerPropTypes,
} from 'src/components/account-ledger/account-ledger';
import { getAccountDetail } from 'src/urls/frontend-royalties';
import { smartFormatter } from 'src/utils/amount-helpers';
import type { GetLedgerAccountContractListQuery } from 'src/apollo/queries/ledger/__generated__/get-ledger-account-contract-list';
import { ABACUS_PROFILE } from 'src/constants';

describe('<AccountLedger>', () => {
    const identity = createIdentity({ profileType: ABACUS_PROFILE });
    const contracts = contractList.abacusContracts.items;
    let requestSpy: any;

    afterEach(jest.restoreAllMocks);

    beforeEach(() => {
        requestSpy = jest
            .spyOn(ledgerQuery, 'useLedgerAccountContractList')
            .mockReturnValue({
                data: accountContractLedgerList as GetLedgerAccountContractListQuery,
                error: undefined,
                loading: false,
            });
    });

    const render = (props: AccountLedgerPropTypes) =>
        renderInAppContext(
            <Route path="/account/:accountId/">
                <AccountLedger {...props} />
            </Route>,
            {
                pathname: getAccountDetail(1),
                identity,
            }
        );

    const props: AccountLedgerPropTypes = {
        accountContracts: contracts,
        accountCurrencyCode: 'USD',
        payableBalance: '40293.52',
    };

    it('renders payable balance', () => {
        render(props);

        expect(screen.getByText('Payable Balance')).toBeDefined();
        expect(screen.getByTestId('payableBalance').textContent).toEqual(
            '$40,293.52'
        );
    });

    it('renders payment currency', () => {
        render(props);

        const paymentCurrency =
            screen.getByTestId('paymentCurrency').textContent;

        expect(screen.getByText('Payment Currency')).toBeDefined();
        expect(paymentCurrency).toEqual('USD (US Dollar)');
    });

    it('requests an account ledger list on render', () => {
        render(props);

        expect(requestSpy).toHaveBeenCalled();
    });

    it('renders a table headers', () => {
        render(props);

        const tableHeaders = [
            'Date',
            'Event',
            'Accounting Period',
            'Statement Period',
            'Contract',
            'Amount',
        ];
        tableHeaders.forEach((header: any) =>
            expect(screen.getAllByText(header)).toBeDefined()
        );
    });

    it('renders a list of ledgers', () => {
        const bodyText: any = [];

        render(props);
        document
            .querySelectorAll('div.GridTable-cell')
            .forEach((body: any) => bodyText.push(body.textContent));

        expect(bodyText).toEqual([
            '2020-03-31',
            'Payment',
            '-',
            'March 2020',
            'Get Lucky Records',
            smartFormatter('-80059.95', 'USD'),
            '2020-03-31',
            'Tax Withholding',
            '-',
            'March 2020',
            'Get Lucky Records',
            smartFormatter('80059.95', 'USD'),
            '2020-03-11',
            'Accounting Run',
            'March 2020',
            'March 2020',
            'Get Lucky Records',
            smartFormatter('12345.67', 'USD'),
            '2020-01-11',
            'Accounting Run',
            'January 2020',
            'January 2020',
            'Get Lucky Records',
            smartFormatter('67714.28', 'USD'),
        ]);
    });

    it('renders Accounting Period links', () => {
        render(props);

        const accPeriodPageLinks = screen.getAllByTestId(
            'accountLedgerAccPeriod-2'
        ) as HTMLLinkElement[];
        expect(accPeriodPageLinks[0].href).toContain(`/accounting-period/2`);
    });

    it('renders Contract links', () => {
        render(props);

        const contractPageLinks = screen.getAllByTestId(
            'accountLedgerContract-1'
        ) as HTMLLinkElement[];
        expect(contractPageLinks[0].href).toContain(`/contract/1`);
    });

    it('renders error message', () => {
        const graphQLError = new apollo.ApolloError({
            graphQLErrors: undefined,
            networkError: null,
            errorMessage: 'GraphQL error',
        });
        jest.spyOn(ledgerQuery, 'useLedgerAccountContractList').mockReturnValue(
            {
                data: {
                    abacusLedgerAccountContractList: {
                        items: [],
                        totalCount: 0,
                    },
                },
                error: graphQLError,
                loading: false,
            }
        );

        render(props);
        expect(screen.getByText(ACCOUNT_LEDGER_ERROR_MESSAGE)).toBeDefined();
        expect(screen.getByText(NO_ACCOUNT_LEDGER_RESULTS)).toBeDefined();
    });

    it('renders filters', () => {
        render(props);

        expect(screen.getAllByText('Event')).toBeDefined();
        expect(screen.getAllByText('Statement Period')).toBeDefined();
        expect(screen.getAllByText('Contract')).toBeDefined();
    });

    it('renders "Clear Filter" link when contract filter is selected', () => {
        render(props);

        const contractsSelect = screen.getAllByRole('combobox', {
            hidden: true,
        })[0];
        fireEvent.click(contractsSelect);
        const selectContractOption = screen.getAllByText(
            `${contracts[0].contractName} (ID ${contracts[0].contractId})`
        );
        fireEvent.click(selectContractOption[0]);

        const applyButton = screen.getByText('Apply');
        fireEvent.click(applyButton);

        expect(screen.getAllByText('Clear filters')).toBeDefined();
    });

    it('renders "Clear Filter" link when event filter is selected', () => {
        render(props);

        const ledgerEventsSelect = screen.getAllByRole('combobox', {
            hidden: true,
        })[1];
        fireEvent.click(ledgerEventsSelect);

        const trackInput = screen.getByTestId('SuiteListViewFilterInput')!;
        fireEvent.change(trackInput, { target: { value: 'Tax Withholding' } });

        const selectAll = screen.getByText('Select all');
        fireEvent.click(selectAll);

        const applyButton = screen.getByText('Apply');
        fireEvent.click(applyButton);

        expect(screen.getAllByText('Clear filters')).toBeDefined();
    });

    it('renders "Clear Filter" link when statement period filter is selected', () => {
        jest.spyOn(
            statementPeriodQuery,
            'useStatementPeriodsList'
        ).mockReturnValue({
            data: statementPeriods,
            loading: false,
            error: undefined,
        });
        render(props);

        const statementPeriodsSelect = screen.getAllByRole('combobox', {
            hidden: true,
        })[2];
        fireEvent.click(statementPeriodsSelect);
        const selectStatementPeriodOption =
            screen.getAllByText('February 2020');

        fireEvent.click(selectStatementPeriodOption[0]);

        const applyButton = screen.getByText('Apply');
        fireEvent.click(applyButton);

        expect(screen.getAllByText('Clear filters')).toBeDefined();
    });
});
