import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { Executor } from './executor';
import type { Alpaca } from './alpaca';
import type { Strategy, Signal } from './strategy';

function mockAlpaca(): Alpaca {
    return {
        getAccount: vi.fn(),
        getAssets: vi.fn(),
        getAsset: vi.fn(),
        getClock: vi.fn(),
        getLatestQuote: vi.fn(),
        getLatestTrades: vi.fn(),
    } as unknown as Alpaca;
}

function mockStrategy(overrides: Partial<Strategy> = {}): Strategy {
    return {
        name: 'test-strategy',
        capitalAllocation: 0.5,
        execute: vi.fn().mockResolvedValue([]),
        ...overrides,
    };
}

describe('Executor', () => {
    beforeEach(() => {
        vi.spyOn(console, 'log').mockImplementation(() => {});
    });

    afterEach(() => {
        vi.restoreAllMocks();
    });

    it('logs a buy signal with correct dollar amount', async () => {
        const executor = new Executor(mockAlpaca());
        const strategy = mockStrategy({ name: 'momentum', capitalAllocation: 0.5 });
        const signals: Signal[] = [{ symbol: 'TQQQ', direction: 'buy', allocation: 0.8 }];

        await executor.executeSignals(strategy, signals, 10000);

        // 10000 * 0.5 * 0.8 = 4000
        expect(console.log).toHaveBeenCalledWith('[momentum] BUY TQQQ — $4000.00');
    });

    it('logs a sell signal with correct dollar amount', async () => {
        const executor = new Executor(mockAlpaca());
        const strategy = mockStrategy({ name: 'rebalance', capitalAllocation: 0.3 });
        const signals: Signal[] = [{ symbol: 'SPY', direction: 'sell', allocation: 1.0 }];

        await executor.executeSignals(strategy, signals, 20000);

        // 20000 * 0.3 * 1.0 = 6000
        expect(console.log).toHaveBeenCalledWith('[rebalance] SELL SPY — $6000.00');
    });

    it('ignores signals with 0 allocation', async () => {
        const executor = new Executor(mockAlpaca());
        const strategy = mockStrategy();
        const signals: Signal[] = [{ symbol: 'TQQQ', direction: 'buy', allocation: 0 }];

        await executor.executeSignals(strategy, signals, 10000);

        expect(console.log).not.toHaveBeenCalled();
    });

    it('handles multiple signals in one batch', async () => {
        const executor = new Executor(mockAlpaca());
        const strategy = mockStrategy({ name: 'multi', capitalAllocation: 0.4 });
        const signals: Signal[] = [
            { symbol: 'TQQQ', direction: 'buy', allocation: 0.5 },
            { symbol: 'SPY', direction: 'sell', allocation: 0.5 },
        ];

        await executor.executeSignals(strategy, signals, 10000);

        // 10000 * 0.4 * 0.5 = 2000 each
        expect(console.log).toHaveBeenCalledWith('[multi] BUY TQQQ — $2000.00');
        expect(console.log).toHaveBeenCalledWith('[multi] SELL SPY — $2000.00');
    });
});
