import { describe, expect, it } from 'vitest';
import {
    assignLifecycleTradeIds,
    isLifecycleFilledStatus,
    mergeOrders,
    normalizeAction,
    normalizeOrder,
    normalizeSide,
    normalizeSource,
    orderKey,
    parseDateEnd,
    parseDateStart,
    pickMostReliableStatus,
    statusRank,
    toEpoch,
    toPositiveNumber
} from './PositionsTab';

const mkOrder = (overrides: Record<string, unknown> = {}) => ({
    id: 'ord-1',
    orderId: 'ord-1',
    symbol: 'BTC/USDT',
    type: 'Market',
    side: 'BUY',
    qty: 1,
    price: 100,
    status: 'pending_new',
    timestamp: 1,
    profileId: 'p1',
    source: 'BOT',
    ...overrides
}) as any;

describe('PositionsTab helper functions', () => {
    it('normalizes sides, actions, sources and numeric/date helpers', () => {
        expect(normalizeSide('sell')).toBe('SELL');
        expect(normalizeSide('short')).toBe('SELL');
        expect(normalizeSide('buy')).toBe('BUY');

        expect(normalizeAction('entry')).toBe('ENTRY');
        expect(normalizeAction('EXIT')).toBe('EXIT');
        expect(normalizeAction('hold')).toBeUndefined();

        expect(normalizeSource('BOT', undefined)).toBe('BOT');
        expect(normalizeSource('manual', undefined)).toBe('MANUAL');
        expect(normalizeSource(undefined, 'p1')).toBe('BOT');
        expect(normalizeSource(undefined, undefined)).toBe('MANUAL');

        expect(toEpoch(123)).toBe(123);
        expect(toEpoch('2026-02-15T00:00:00.000Z')).toBeGreaterThan(0);
        expect(toEpoch('not-a-date')).toBe(0);
        expect(toEpoch(undefined)).toBe(0);

        expect(parseDateStart('')).toBeNull();
        expect(parseDateEnd('')).toBeNull();
        expect(parseDateStart('2026-02-15')).toBeGreaterThan(0);
        expect(parseDateEnd('2026-02-15')).toBeGreaterThan(0);

        expect(toPositiveNumber(10)).toBe(10);
        expect(toPositiveNumber('4.2')).toBe(4.2);
        expect(toPositiveNumber(0)).toBe(0);
        expect(toPositiveNumber(-1)).toBe(0);
        expect(toPositiveNumber('x')).toBe(0);
    });

    it('ranks order statuses and picks the most reliable status', () => {
        expect(isLifecycleFilledStatus('filled')).toBe(true);
        expect(isLifecycleFilledStatus('partially_filled')).toBe(true);
        expect(isLifecycleFilledStatus('pending_new')).toBe(false);

        expect(statusRank('filled')).toBe(6);
        expect(statusRank('partially_filled')).toBe(5);
        expect(statusRank('canceled')).toBe(4);
        expect(statusRank('cancelled')).toBe(4);
        expect(statusRank('rejected')).toBe(4);
        expect(statusRank('expired')).toBe(4);
        expect(statusRank('pending_new')).toBe(2);
        expect(statusRank('pending')).toBe(2);
        expect(statusRank('accepted')).toBe(2);
        expect(statusRank('new')).toBe(2);
        expect(statusRank('unknown')).toBe(1);
        expect(statusRank('something_else')).toBe(2);

        expect(
            pickMostReliableStatus(
                mkOrder({ status: '' }),
                mkOrder({ status: 'accepted' })
            )
        ).toBe('accepted');
        expect(
            pickMostReliableStatus(
                mkOrder({ status: 'filled' }),
                mkOrder({ status: '' })
            )
        ).toBe('filled');
        expect(
            pickMostReliableStatus(
                mkOrder({ status: 'pending_new', timestamp: 1 }),
                mkOrder({ status: 'filled', timestamp: 2 })
            )
        ).toBe('filled');
        expect(
            pickMostReliableStatus(
                mkOrder({ status: 'filled', timestamp: 2 }),
                mkOrder({ status: 'pending_new', timestamp: 3 })
            )
        ).toBe('filled');
        expect(
            pickMostReliableStatus(
                mkOrder({ status: 'accepted', timestamp: 2 }),
                mkOrder({ status: 'new', timestamp: 3 })
            )
        ).toBe('new');
        expect(
            pickMostReliableStatus(
                mkOrder({ status: 'accepted', timestamp: 5 }),
                mkOrder({ status: 'new', timestamp: 3 })
            )
        ).toBe('accepted');
    });

    it('normalizes and merges orders with deterministic keys', () => {
        expect(normalizeOrder({ symbol: 'BTC/USDT' } as any)).toBeNull();
        expect(normalizeOrder({ id: 'id-only' } as any)).toBeNull();

        const normalized = normalizeOrder({
            id: 'order-a',
            profile_id: 'p1',
            symbol: 'ETH/USDT',
            side: 'short',
            quantity: 2,
            price: '101.5' as any,
            status: 'FILLED',
            timestamp: '2026-02-15T12:00:00.000Z',
            action: 'hold',
            stop_loss: 95,
            take_profit: 110
        } as any);
        expect(normalized).toEqual(expect.objectContaining({
            id: 'order-a',
            side: 'SELL',
            qty: 2,
            price: 101.5,
            status: 'filled',
            action: undefined,
            source: 'BOT',
            stopLoss: 95,
            takeProfit: 110
        }));

        const base = mkOrder({
            id: 'order-a',
            orderId: 'order-a',
            qty: 0,
            price: 0,
            status: 'pending_new',
            timestamp: 10,
            action: undefined,
            stopLoss: 0,
            takeProfit: 0
        });
        const incoming = mkOrder({
            id: 'order-a',
            orderId: 'order-a',
            qty: 2.5,
            price: 101,
            status: 'filled',
            timestamp: 20,
            action: 'ENTRY',
            stopLoss: 90,
            takeProfit: 120
        });
        const merged = mergeOrders(base, incoming);
        expect(orderKey(merged)).toBe('id:order-a');
        expect(merged).toEqual(expect.objectContaining({
            qty: 2.5,
            price: 101,
            status: 'filled',
            action: 'ENTRY',
            stopLoss: 90,
            takeProfit: 120
        }));

        const mergedWithOlderIncoming = mergeOrders(
            mkOrder({ id: 'order-b', orderId: 'order-b', qty: 1.25, price: 88, timestamp: 50 }),
            mkOrder({ id: 'order-b', orderId: 'order-b', qty: 0, price: 0, timestamp: 10 })
        );
        expect(mergedWithOlderIncoming.qty).toBe(1.25);
        expect(mergedWithOlderIncoming.price).toBe(88);
    });

    it('assigns lifecycle trade ids and actions for mixed legacy and explicit orders', () => {
        const inputOrders = [
            mkOrder({
                id: 'm0',
                orderId: 'm0',
                profileId: 'p-branch',
                symbol: 'BNB/USDT',
                side: 'BUY',
                action: undefined,
                tradeId: 'BR-1',
                timestamp: 0
            }),
            mkOrder({
                id: 'm1',
                orderId: 'm1',
                profileId: 'p-live',
                symbol: 'SOL/USDT',
                side: 'BUY',
                action: undefined,
                tradeId: undefined,
                timestamp: 1
            }),
            mkOrder({
                id: 'm2',
                orderId: 'm2',
                profileId: 'p-live',
                symbol: 'SOL/USDT',
                side: 'SELL',
                action: undefined,
                tradeId: 'LIVE-1',
                timestamp: 2
            }),
            mkOrder({
                id: 'm3',
                orderId: 'm3',
                profileId: 'p-queue',
                symbol: 'ADA/USDT',
                side: 'BUY',
                action: undefined,
                tradeId: undefined,
                timestamp: 3
            }),
            mkOrder({
                id: 'm4',
                orderId: 'm4',
                profileId: 'p-queue',
                symbol: 'ADA/USDT',
                side: 'SELL',
                action: undefined,
                tradeId: undefined,
                timestamp: 4
            }),
            mkOrder({
                id: 'm5',
                orderId: 'm5',
                profileId: 'p-explicit',
                symbol: 'XRP/USDT',
                side: 'BUY',
                action: 'ENTRY',
                tradeId: 'EX-1',
                timestamp: 5
            }),
            mkOrder({
                id: 'm6',
                orderId: 'm6',
                profileId: 'p-explicit',
                symbol: 'XRP/USDT',
                side: 'SELL',
                action: 'EXIT',
                tradeId: 'EX-1',
                timestamp: 6
            }),
            mkOrder({
                id: 'm7',
                orderId: 'm7',
                profileId: 'p-shift',
                symbol: 'DOGE/USDT',
                side: 'BUY',
                action: 'ENTRY',
                tradeId: 'SHIFT-A',
                timestamp: 7
            }),
            mkOrder({
                id: 'm8',
                orderId: 'm8',
                profileId: 'p-shift',
                symbol: 'DOGE/USDT',
                side: 'SELL',
                action: 'EXIT',
                tradeId: 'SHIFT-OTHER',
                timestamp: 8
            })
        ];

        const output = assignLifecycleTradeIds(inputOrders, [
            {
                profileId: 'p-live',
                symbol: 'SOL/USDT',
                side: 'BUY',
                tradeId: 'LIVE-1'
            } as any
        ]);

        const byId = new Map(output.map((row) => [row.id, row]));
        expect(byId.get('m0')?.action).toBe('ENTRY');
        expect(byId.get('m1')?.tradeId).toBe('LIVE-1');
        expect(byId.get('m1')?.action).toBe('ENTRY');
        expect(byId.get('m2')?.action).toBe('EXIT');

        expect(byId.get('m3')?.tradeId).toMatch(/^TRD-LEGACY-p-queue-ADA\/USDT-/);
        expect(byId.get('m3')?.action).toBe('ENTRY');
        expect(byId.get('m4')?.action).toBe('EXIT');
        expect(byId.get('m4')?.tradeId).toBe(byId.get('m3')?.tradeId);

        expect(byId.get('m5')?.action).toBe('ENTRY');
        expect(byId.get('m6')?.action).toBe('EXIT');
        expect(byId.get('m7')?.action).toBe('ENTRY');
        expect(byId.get('m8')?.action).toBe('EXIT');
    });
});
