// @vitest-environment jsdom
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { EntryForm } from './EntryForm';

const {
    getPlatformAccessTokenMock,
    createManualEntryMock,
    updateManualEntryMock,
    authMock
} = vi.hoisted(() => ({
    getPlatformAccessTokenMock: vi.fn(),
    createManualEntryMock: vi.fn(),
    updateManualEntryMock: vi.fn(),
    authMock: { user: { id: 'user-1' } as any }
}));

vi.mock('../components/AuthContext', () => ({
    useAuth: () => authMock
}));

vi.mock('../lib/authSession', () => ({
    getPlatformAccessToken: getPlatformAccessTokenMock
}));

vi.mock('../lib/manualEntriesApi', () => ({
    createManualEntry: createManualEntryMock,
    updateManualEntry: updateManualEntryMock
}));

describe('EntryForm DOM flow', () => {
    const alertMock = vi.fn();

    beforeEach(() => {
        vi.clearAllMocks();
        createManualEntryMock.mockReset();
        updateManualEntryMock.mockReset();
        getPlatformAccessTokenMock.mockReset();
        authMock.user = { id: 'user-1' };

        createManualEntryMock.mockResolvedValue({});
        updateManualEntryMock.mockResolvedValue({});
        getPlatformAccessTokenMock.mockRejectedValue(new Error('Not authenticated'));

        vi.stubGlobal('fetch', vi.fn());
        vi.stubGlobal('confirm', vi.fn(() => true));
        vi.stubGlobal('alert', alertMock);
        vi.spyOn(crypto, 'randomUUID').mockReturnValue('11111111-1111-1111-1111-111111111111');
    });

    it('inserts a new manual entry with numeric normalization', async () => {
        const onSuccess = vi.fn();
        const user = userEvent.setup();

        render(<EntryForm onSuccess={onSuccess} />);

        await user.type(screen.getByPlaceholderText('BTC/USD'), 'BTC/USD');
        await user.type(screen.getByPlaceholderText('0.00'), '100');
        await user.type(screen.getByPlaceholderText('0'), '1');
        await user.click(screen.getByRole('button', { name: 'Add' }));

        await waitFor(() => {
            expect(createManualEntryMock).toHaveBeenCalled();
            expect(onSuccess).toHaveBeenCalled();
        });
    });

    it('alerts error when trade execution fails', async () => {
        const onSuccess = vi.fn();
        const user = userEvent.setup();
        getPlatformAccessTokenMock.mockResolvedValue('valid-token');
        vi.mocked(fetch).mockResolvedValue({
            ok: false,
            json: async () => ({ success: false, error: 'Insufficient funds' })
        } as any);

        render(<EntryForm onSuccess={onSuccess} />);
        await user.type(screen.getByPlaceholderText('BTC/USD'), 'BTC/USD');
        await user.type(screen.getByPlaceholderText('0'), '1');
        await user.click(screen.getByRole('checkbox', { name: /Execute/i }));
        await user.click(screen.getByRole('button', { name: 'Add' }));

        await waitFor(() => {
            expect(alertMock).toHaveBeenCalledWith(expect.stringContaining('Execution Failed: Insufficient funds'));
        });
    });

    it('journals a past trade (entry + exit both provided)', async () => {
        const onSuccess = vi.fn();
        const user = userEvent.setup();

        render(<EntryForm onSuccess={onSuccess} />);
        await user.type(screen.getByPlaceholderText('BTC/USD'), 'BTC/USD');
        await user.type(screen.getByPlaceholderText('0.00'), '100');
        await user.type(screen.getByPlaceholderText('Exit'), '110');
        await user.type(screen.getByPlaceholderText('0'), '1');
        await user.click(screen.getByRole('button', { name: 'Add' }));

        await waitFor(() => {
            expect(createManualEntryMock).toHaveBeenCalledTimes(1);
        });
        expect(createManualEntryMock.mock.calls[0][0]).toEqual(expect.objectContaining({
            symbol: 'BTC/USD',
            buy_price: 100,
            sell_price: 110
        }));
    });

    it('handles database error by alerting user', async () => {
        createManualEntryMock.mockRejectedValueOnce(new Error('DB Error'));
        const user = userEvent.setup();

        render(<EntryForm onSuccess={vi.fn()} />);
        await user.type(screen.getByPlaceholderText('BTC/USD'), 'FAIL/USD');
        await user.click(screen.getByRole('button', { name: 'Add' }));

        await waitFor(() => {
            expect(alertMock).toHaveBeenCalledWith(expect.stringContaining('DB Error'));
        });
    });

    it('does nothing when submitted without a user', async () => {
        authMock.user = null;
        const user = userEvent.setup();
        render(<EntryForm onSuccess={vi.fn()} />);
        await user.type(screen.getByPlaceholderText('BTC/USD'), 'BTC/USD');
        await user.click(screen.getByRole('button', { name: 'Add' }));

        expect(createManualEntryMock).not.toHaveBeenCalled();
    });
});
