import assert from 'node:assert/strict';
import { OrderStatusSyncService, type OrderStatusSyncEvent } from '../src/services/OrderStatusSyncService.js';
import type { Candle, IExchangeConnector } from '../src/connectors/types.js';
import { supabaseService } from '../src/services/SupabaseService.js';
import { config } from '../src/config/index.js';

class MockOrderSyncExchange implements IExchangeConnector {
    private orderResponses = new Map<string, any>();

    setOrder(orderId: string, payload: any) {
        this.orderResponses.set(orderId, payload);
    }

    async fetchOHLCV(_symbol: string, _timeframe: string, _limit?: number): Promise<Candle[]> {
        return [{
            timestamp: Date.now(),
            open: 100,
            high: 100,
            low: 100,
            close: 100,
            volume: 1
        }];
    }

    async placeOrder(_symbol: string, _side: 'buy' | 'sell', qty: number, _type: 'market' | 'limit', price?: number): Promise<any> {
        return {
            id: `mock-order-${Date.now()}`,
            status: 'filled',
            filled_avg_price: price || 100,
            filled_qty: qty
        };
    }

    async getPosition(_symbol: string): Promise<any> {
        return null;
    }

    async getOrder(orderId: string): Promise<any> {
        const response = this.orderResponses.get(orderId);
        if (response instanceof Error) {
            throw response;
        }
        return response ?? null;
    }

    cancelOrder = async (_orderId: string, _symbol?: string): Promise<boolean> => true;

    getCapabilities = () => ({
        fetchOpenOrders: true,
        fetchOrders: true,
        fetchClosedOrders: true,
        fetchMyTrades: true,
        fetchOHLCV: true,
        cancelOrder: true,
        createOrder: true,
        editOrder: false,
        fetchBalance: true,
        fetchLedger: false,
        fetchTicker: true,
        fetchTickers: true,
        fetchPosition: true,
        fetchPositions: true,
        shorting: true
    });
}

async function run() {
    const exchange = new MockOrderSyncExchange();
    const seenEvents: OrderStatusSyncEvent[] = [];
    const updateCalls: Array<{ orderId: string; status: string; fillQty?: number }> = [];
    const previousMissingGrace = config.ORDER_SYNC_MISSING_GRACE_MINUTES;
    const previousMissingConfirmations = config.ORDER_SYNC_MISSING_CONFIRMATION_COUNT;

    const originalGetStaleOrders = (supabaseService as any).getStaleOrders;
    const originalUpdateOrderStatus = (supabaseService as any).updateOrderStatus;
    const originalIsTradeLifecycleClosed = (supabaseService as any).isTradeLifecycleClosed;
    const originalGetVirtualOpenPosition = (supabaseService as any).getVirtualOpenPosition;

    const staleRows = [
        {
            order_id: 'ord-partial',
            symbol: 'BTC/USD',
            status: 'pending_new',
            action: 'EXIT',
            trade_id: 'TRD-sync-1',
            user_id: 'user-1',
            profile_id: 'profile-1',
            created_at: new Date(Date.now() - 15 * 60 * 1000).toISOString()
        },
        {
            order_id: 'ord-missing-old',
            symbol: 'ETH/USD',
            status: 'pending_new',
            action: 'ENTRY',
            trade_id: 'TRD-sync-2',
            user_id: 'user-1',
            profile_id: 'profile-1',
            created_at: new Date(Date.now() - 26 * 60 * 60 * 1000).toISOString()
        },
        {
            order_id: 'ord-ghost-exit',
            symbol: 'BTC/USD',
            status: 'pending_new',
            action: 'EXIT',
            trade_id: 'TRD-sync-closed',
            user_id: 'user-1',
            profile_id: 'profile-1',
            created_at: new Date(Date.now() - 15 * 60 * 1000).toISOString()
        },
        {
            order_id: 'ord-legacy-closed-error',
            symbol: 'ETH/USD',
            status: 'pending_new',
            trade_id: 'TRD-sync-closed-error',
            user_id: 'user-1',
            profile_id: 'profile-1',
            created_at: new Date(Date.now() - 20 * 60 * 1000).toISOString()
        },
        {
            order_id: 'ord-transient-failure',
            symbol: 'ETH/USD',
            status: 'pending_new',
            action: 'EXIT',
            trade_id: 'TRD-sync-timeout',
            user_id: 'user-1',
            profile_id: 'profile-1',
            created_at: new Date(Date.now() - 20 * 60 * 1000).toISOString()
        },
        {
            order_id: 'ord-legacy-no-trade',
            symbol: 'SOL/USD',
            side: 'SELL',
            status: 'pending_new',
            action: 'EXIT',
            user_id: 'user-1',
            profile_id: 'profile-1',
            created_at: new Date(Date.now() - 30 * 60 * 1000).toISOString()
        }
    ];

    exchange.setOrder('ord-partial', {
        id: 'ord-partial',
        status: 'partially_filled',
        filled_avg_price: 101.25,
        filled_qty: '0.35'
    });
    exchange.setOrder('ord-missing-old', null);
    exchange.setOrder('ord-legacy-closed-error', new Error('404 order not found'));
    exchange.setOrder('ord-transient-failure', new Error('socket timeout'));
    exchange.setOrder('ord-legacy-no-trade', null);

    (supabaseService as any).getStaleOrders = async () => staleRows;
    (supabaseService as any).isTradeLifecycleClosed = async (tradeId: string) =>
        tradeId === 'TRD-sync-closed' || tradeId === 'TRD-sync-closed-error';
    (supabaseService as any).getVirtualOpenPosition = async (profileId: string, symbol: string) => {
        if (profileId === 'profile-1' && symbol === 'SOL/USD') {
            return null;
        }
        return {
            profileId,
            symbol,
            side: 'BUY',
            qty: 1,
            entryPrice: 100,
            stopLoss: 90,
            takeProfit: 110,
            tradeId: 'TRD-open'
        };
    };
    (supabaseService as any).updateOrderStatus = async (
        orderId: string,
        status: string,
        _filledAt?: Date,
        _price?: number,
        qty?: number
    ) => {
        updateCalls.push({ orderId, status, fillQty: qty });
    };

    try {
        (config as any).ORDER_SYNC_MISSING_GRACE_MINUTES = 0;
        (config as any).ORDER_SYNC_MISSING_CONFIRMATION_COUNT = 2;

        const service = new OrderStatusSyncService(
            exchange,
            10_000,
            'profile-1',
            (event) => {
                seenEvents.push(event);
            }
        );

        await service.triggerSync();

        const partialUpdate = updateCalls.find((c) => c.orderId === 'ord-partial');
        assert(partialUpdate, 'Partial-fill stale order should be updated.');
        assert.equal(partialUpdate.status, 'partially_filled');
        assert.equal(Number(partialUpdate.fillQty), 0.35, 'Partial-fill quantity should be preserved in status update.');

        const partialEvent = seenEvents.find((e) => e.orderId === 'ord-partial');
        assert(partialEvent, 'Partial-fill stale order should emit lifecycle callback.');
        assert.equal(partialEvent?.status, 'partially_filled');
        assert.equal(Number(partialEvent?.fillQty), 0.35);
        assert.equal(Boolean(partialEvent?.quarantined), false);

        assert.equal(
            Boolean(updateCalls.find((c) => c.orderId === 'ord-missing-old')),
            false,
            'Missing orders should not mutate on first detection in confirmation mode.'
        );

        await service.triggerSync();

        const missingOldUpdate = updateCalls.find((c) => c.orderId === 'ord-missing-old');
        assert(missingOldUpdate, 'Old missing order should be quarantined to unknown.');
        assert.equal(missingOldUpdate.status, 'unknown');

        const missingOldEvent = seenEvents.find((e) => e.orderId === 'ord-missing-old');
        assert(missingOldEvent, 'Old missing order should emit quarantined callback.');
        assert.equal(missingOldEvent?.status, 'unknown');
        assert.equal(Boolean(missingOldEvent?.quarantined), true);

        const ghostExitUpdate = updateCalls.find((c) => c.orderId === 'ord-ghost-exit');
        assert(ghostExitUpdate, 'Closed lifecycle ghost EXIT should be auto-resolved.');
        assert.equal(ghostExitUpdate.status, 'canceled');

        const ghostExitEvent = seenEvents.find((e) => e.orderId === 'ord-ghost-exit');
        assert(ghostExitEvent, 'Closed lifecycle ghost EXIT should emit callback.');
        assert.equal(ghostExitEvent?.status, 'canceled');
        assert.equal(Boolean(ghostExitEvent?.quarantined), false);

        const legacyClosedUpdate = updateCalls.find((c) => c.orderId === 'ord-legacy-closed-error');
        assert(legacyClosedUpdate, 'Legacy closed lifecycle with exchange not-found exception should be auto-resolved.');
        assert.equal(legacyClosedUpdate.status, 'canceled');

        const legacyClosedEvent = seenEvents.find((e) => e.orderId === 'ord-legacy-closed-error');
        assert(legacyClosedEvent, 'Legacy closed lifecycle should emit callback.');
        assert.equal(legacyClosedEvent?.status, 'canceled');
        assert.equal(Boolean(legacyClosedEvent?.quarantined), false);

        const transientFailureUpdate = updateCalls.find((c) => c.orderId === 'ord-transient-failure');
        assert.equal(Boolean(transientFailureUpdate), false, 'Transient exchange failures must not mutate order status.');

        const legacyNoTradeUpdate = updateCalls.find((c) => c.orderId === 'ord-legacy-no-trade');
        assert(legacyNoTradeUpdate, 'Legacy EXIT-like order without trade_id should be auto-resolved when profile lifecycle is flat.');
        assert.equal(legacyNoTradeUpdate.status, 'canceled');

        const legacyNoTradeEvent = seenEvents.find((e) => e.orderId === 'ord-legacy-no-trade');
        assert(legacyNoTradeEvent, 'Legacy EXIT-like order without trade_id should emit callback.');
        assert.equal(legacyNoTradeEvent?.status, 'canceled');
        assert.equal(Boolean(legacyNoTradeEvent?.quarantined), false);
    } finally {
        (config as any).ORDER_SYNC_MISSING_GRACE_MINUTES = previousMissingGrace;
        (config as any).ORDER_SYNC_MISSING_CONFIRMATION_COUNT = previousMissingConfirmations;
        (supabaseService as any).getStaleOrders = originalGetStaleOrders;
        (supabaseService as any).updateOrderStatus = originalUpdateOrderStatus;
        (supabaseService as any).isTradeLifecycleClosed = originalIsTradeLifecycleClosed;
        (supabaseService as any).getVirtualOpenPosition = originalGetVirtualOpenPosition;
    }

    console.log('[order-status-sync-regressions] OK: stale partial fill, ghost EXIT resolution, and quarantine paths validated');
}

await run();
