import React from 'react';
import { fireEvent, screen, waitFor } from '@testing-library/react';
import { createIdentity, renderInAppContext } from '@theorchard/suite-testing';
import { useAccountContracts } from 'src/apollo/queries/account';
import { ABACUS_PROFILE } from 'src/constants';
import CreateTransferForm from '../create-transfer/create-transfer-form';

// Mock the data hooks so we can drive the form through the destination-contract
// step. useAccountContracts is configurable per-test (1 vs 2 contracts).
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 },
                  },
    }),
    useAccountContracts: jest.fn(() => ({ contracts: [] })),
    useAccountSubaccounts: () => ({
        subaccounts: [{ subaccountId: 88046, name: 'Sub A' }],
    }),
}));

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/subaccount match the From selection so the
    // scope guard surfaces it.
    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 mockUseAccountContracts = useAccountContracts as jest.Mock;

const contract = (id: string, name: string) => ({
    contractId: id,
    contractName: name,
    contractType: 'Standard',
});

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 — destination contract', () => {
    it('keeps Next disabled and offers no bypass until a contract is picked', async () => {
        // Two contracts: the user must pick one (no auto-select).
        mockUseAccountContracts.mockReturnValue({
            contracts: [
                contract('c-1', 'Contract One'),
                contract('c-2', 'Contract Two'),
            ],
        });

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

        await openAndSelect('Select Account', 'From Co');
        await openAndSelect('Select Subaccount', 'Sub A');
        await typeAndSelectProject('Proj', 'Proj');
        await openAndSelect('Select Account', 'To Co');

        // Details are complete but no contract is selected yet.
        expect(screen.getByRole('button', { name: 'Next' })).toBeDisabled();
        // The old "submit without a contract" bypass must be gone.
        expect(
            screen.queryByRole('button', { name: 'Submit Transfer' })
        ).not.toBeInTheDocument();

        await openAndSelect('Select Contract', 'Contract One');

        // With a contract chosen, the user can proceed.
        expect(screen.getByRole('button', { name: 'Next' })).toBeEnabled();
    });

    it('capitalizes the contract type shown in the selector', async () => {
        mockUseAccountContracts.mockReturnValue({
            contracts: [
                {
                    contractId: 'c-1',
                    contractName: 'Contract One',
                    contractType: 'distribution',
                },
                {
                    contractId: 'c-2',
                    contractName: 'Contract Two',
                    contractType: 'distribution',
                },
            ],
        });

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

        await openAndSelect('Select Account', 'From Co');
        await openAndSelect('Select Subaccount', 'Sub A');
        await typeAndSelectProject('Proj', 'Proj');
        await openAndSelect('Select Account', 'To Co');
        fireEvent.click(screen.getByText('Select Contract'));

        expect(
            (await screen.findAllByText('Distribution')).length
        ).toBeGreaterThan(0);
        expect(screen.queryByText('distribution')).not.toBeInTheDocument();
    });

    it('auto-selects the only contract so the user can proceed without picking', async () => {
        // Exactly one contract -> auto-selected.
        mockUseAccountContracts.mockReturnValue({
            contracts: [contract('c-1', 'Contract One')],
        });

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

        await openAndSelect('Select Account', 'From Co');
        await openAndSelect('Select Subaccount', 'Sub A');
        await typeAndSelectProject('Proj', 'Proj');
        await openAndSelect('Select Account', 'To Co');

        // No manual contract pick: the single contract is auto-selected, so
        // Next becomes enabled on its own.
        await waitFor(() =>
            expect(screen.getByRole('button', { name: 'Next' })).toBeEnabled()
        );
    });

    it('shows "Loading contracts..." while the contracts query is in flight', async () => {
        // Contracts still loading: contractId can't be set yet, so Next is
        // blocked. The tooltip should reflect the transient load rather than
        // "Select a contract" (nothing is selectable yet).
        mockUseAccountContracts.mockReturnValue({
            contracts: [],
            loading: true,
        });

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

        await openAndSelect('Select Account', 'From Co');
        await openAndSelect('Select Subaccount', 'Sub A');
        await typeAndSelectProject('Proj', 'Proj');
        await openAndSelect('Select Account', 'To Co');

        expect(screen.getByRole('button', { name: 'Next' })).toBeDisabled();
        fireEvent.mouseOver(
            document.querySelector('#next-btn-tooltip') as Element
        );
        expect(
            await screen.findByText('Loading contracts...')
        ).toBeInTheDocument();
    });

    it('explains that the destination account has no contracts when stuck', async () => {
        // Zero contracts: contractId can never be set, so Next stays disabled.
        // The tooltip must say why instead of "Select a contract to proceed to
        // terms" (which implies there is one to pick).
        mockUseAccountContracts.mockReturnValue({
            contracts: [],
            loading: false,
        });

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

        await openAndSelect('Select Account', 'From Co');
        await openAndSelect('Select Subaccount', 'Sub A');
        await typeAndSelectProject('Proj', 'Proj');
        await openAndSelect('Select Account', 'To Co');

        expect(screen.getByRole('button', { name: 'Next' })).toBeDisabled();
        fireEvent.mouseOver(
            document.querySelector('#next-btn-tooltip') as Element
        );
        expect(
            await screen.findByText('The destination account has no contracts')
        ).toBeInTheDocument();
    });
});
