import React from 'react';
import { fireEvent, screen, waitFor } from '@testing-library/react';
import { createIdentity, renderInAppContext } from '@theorchard/suite-testing';
import * as storesQuery from 'src/apollo/queries/stores';
import * as txnQueries from 'src/apollo/queries/transaction-types';
import { ABACUS_PROFILE } from 'src/constants';
import CreateTransferForm from '../create-transfer/create-transfer-form';
import type { GetStoresQuery } from 'src/apollo/queries/__generated__/stores';

// Drive the form to the Terms step (mirrors create-transfer-contract-required).
jest.mock('src/apollo/queries/account', () => ({
    useAccountsForTransferSearch: () => ({
        search: async () => ({
            data: [
                { label: 'From Co', value: 'from-1', subtitle: '101' },
                { label: 'To Co', value: 'to-1', subtitle: '202' },
            ],
            totalCount: 2,
        }),
        getAccount: (id: string) =>
            id === 'from-1'
                ? {
                      accountId: 'from-1',
                      accountName: 'From Co',
                      vendor: { vendorId: 101 },
                  }
                : {
                      accountId: 'to-1',
                      accountName: 'To Co',
                      vendor: { vendorId: 202 },
                  },
    }),
    // Exactly one contract -> auto-selected, so Next enables on its own.
    useAccountContracts: () => ({
        contracts: [
            {
                contractId: 'c-1',
                contractName: 'Contract One',
                contractType: 'distribution',
            },
        ],
    }),
    useAccountSubaccounts: () => ({ subaccounts: [] }),
}));

jest.mock('src/apollo/queries/product-search', () => ({
    useProjectById: () => jest.fn().mockResolvedValue(null),
    // The picker resolves projects via searchProject, so return the test
    // project here; its vendor matches the From selection so the scope guard
    // surfaces it (this account has no subaccounts).
    useProjectSearch: () =>
        jest.fn().mockResolvedValue([
            {
                projectId: 42,
                projectName: 'Proj',
                projectCode: 'PC',
                productCount: 1,
                vendorId: 101,
                subaccountId: 88046,
            },
        ]),
    useProjectProducts: () => ({
        products: [
            {
                upc: 'u1',
                productName: 'Prod',
                project: {
                    projectId: '42',
                    projectName: 'Proj',
                    projectCode: 'PC',
                },
                tracks: [],
            },
        ],
        loading: false,
    }),
}));

jest.mock('src/apollo/mutations/create-project-transfer', () => ({
    useCreateProjectTransfer: () => ({ create: jest.fn() }),
}));

const identity = createIdentity({ profileType: ABACUS_PROFILE });
const noop = () => {};

const openAndSelect = async (triggerText: string, optionText: string) => {
    fireEvent.click(screen.getByText(triggerText));
    fireEvent.click(await screen.findByText(optionText));
};

// The project picker is type-to-search, so a term must be entered before its
// options appear.
const typeAndSelectProject = async (term: string, optionText: string) => {
    fireEvent.click(screen.getByText('Select Project'));
    fireEvent.change(await screen.findByPlaceholderText('Search project'), {
        target: { value: term },
    });
    fireEvent.click(await screen.findByText(optionText));
};

describe('CreateTransferForm — term validation', () => {
    beforeEach(() => {
        jest.spyOn(storesQuery, 'useStoreList').mockReturnValue({
            data: {
                deliveryStoresV2: { items: [] },
            } as unknown as GetStoresQuery,
            loading: false,
        });
        jest.spyOn(txnQueries, 'useTransactionTypes').mockReturnValue({
            data: { transactionTypes: [] },
            loading: false,
        });
        jest.spyOn(txnQueries, 'useTransactionTypeGroups').mockReturnValue({
            data: { transactionTypeGroups: [] },
            loading: false,
        });
    });
    afterEach(jest.restoreAllMocks);

    const gotoTermsCustomAddProduct = async () => {
        renderInAppContext(
            <CreateTransferForm isOpen onClose={noop} onSuccess={noop} />,
            { identity }
        );

        // This account has no subaccounts, so no From Subaccount step: the
        // project search is vendor-wide.
        await openAndSelect('Select Account', 'From Co');
        await typeAndSelectProject('Proj', 'Proj');
        await openAndSelect('Select Account', 'To Co');

        await waitFor(() =>
            expect(screen.getByRole('button', { name: 'Next' })).toBeEnabled()
        );
        fireEvent.click(screen.getByRole('button', { name: 'Next' }));

        fireEvent.click(
            screen.getByText('Configure specific Product and Track terms')
        );
        fireEvent.click(screen.getByTestId('addProductTerm'));
    };

    it('disables Submit Transfer while a term is invalid and explains why on hover', async () => {
        await gotoTermsCustomAddProduct();

        // No nagging panel; the disabled button is the passive indicator.
        expect(
            screen.queryByText('Term Rate cannot be blank')
        ).not.toBeInTheDocument();

        const submit = screen.getByRole('button', { name: 'Submit Transfer' });
        expect(submit).toBeDisabled();

        // Hovering the disabled button reveals the reasons.
        fireEvent.mouseOver(
            document.querySelector('#submit-transfer-tooltip') as Element
        );
        expect(
            await screen.findByText('Please select product(s)')
        ).toBeInTheDocument();
        expect(
            screen.getByText('Term Rate cannot be blank')
        ).toBeInTheDocument();
    });

    it('turns the Label share input red live when it is out of range', async () => {
        await gotoTermsCustomAddProduct();

        const rate = screen.getByTestId('termRate');
        expect(rate).not.toHaveClass('is-invalid');

        // -1 is out of range: flag the input immediately, no submit needed.
        fireEvent.change(rate, { target: { value: '-1', name: 'termRate' } });
        expect(rate).toHaveClass('is-invalid');

        // A valid share clears it.
        fireEvent.change(rate, { target: { value: '50', name: 'termRate' } });
        expect(rate).not.toHaveClass('is-invalid');
    });
});
