import { config } from '../config/index.js';

export interface TradingControlSnapshot {
    mode: 'RUNNING' | 'PAUSED';
    lastChangedBy: string;
    lastChangedAt: number;
    reason?: string;
}

export interface ReconciliationNoGoSample {
    profileId: string;
    symbol: string;
    tradeId: string;
    reason: string;
}

export interface HealthSnapshot {
    tradingLoopHealthy: boolean;
    tradingLoopLastRun: number | null;
    monitorLoopHealthy: boolean;
    monitorLoopLastRun: number | null;
    orderSyncHealthy: boolean;
    orderSyncLastRun: number | null;
    lockContentionCount: number;
    reconciliationLoopHealthy: boolean;
    reconciliationLoopLastRun: number | null;
    reconciliationMismatchCount: number;
    reconciliationMissingFromExchange: number;
    reconciliationMissingInDb: number;
    reconciliationNoGoTrades: number;
    reconciliationNoGoReasonCounts: Record<string, number>;
    reconciliationNoGoSamples: ReconciliationNoGoSample[];
    reconciliationIntegrityWatchdogTriggered: boolean;
    reconciliationLockContentionCount: number;
    tradingControl: TradingControlSnapshot;
}

export class HealthTracker {
    private tradingLoopLastRun: number | null = null;
    private tradingLoopLastSuccess = true;
    private monitorLoopLastRun: number | null = null;
    private orderSyncLastRun: number | null = null;
    private lockContention = 0;
    private reconciliationLockContention = 0;
    private reconciliationLastRun: number | null = null;
    private reconciliationLastSuccess = true;
    private reconciliationMismatchCount = 0;
    private reconciliationMissingFromExchange = 0;
    private reconciliationMissingInDb = 0;
    private reconciliationNoGoTrades = 0;
    private reconciliationNoGoReasonCounts: Record<string, number> = {};
    private reconciliationNoGoSamples: ReconciliationNoGoSample[] = [];
    private reconciliationIntegrityWatchdogTriggered = false;
    private tradingControlListeners = new Set<(update: TradingControlSnapshot) => void>();
    private tradingControl: TradingControlSnapshot = {
        mode: 'RUNNING',
        lastChangedBy: 'system',
        lastChangedAt: Date.now()
    };

    public recordTradingLoop(healthy: boolean) {
        this.tradingLoopLastRun = Date.now();
        this.tradingLoopLastSuccess = healthy;
    }

    public recordMonitorLoop() {
        this.monitorLoopLastRun = Date.now();
    }

    public recordOrderSyncLoop() {
        this.orderSyncLastRun = Date.now();
    }

    public incrementLockContention() {
        this.lockContention += 1;
    }

    public incrementReconciliationLockContention() {
        this.reconciliationLockContention += 1;
    }

    public recordTradingControl(update: TradingControlSnapshot) {
        this.tradingControl = update;
        for (const listener of this.tradingControlListeners) {
            try {
                listener(update);
            } catch {
                // Observers are best-effort. Health state remains authoritative.
            }
        }
    }

    public subscribeTradingControl(listener: (update: TradingControlSnapshot) => void): () => void {
        this.tradingControlListeners.add(listener);
        return () => {
            this.tradingControlListeners.delete(listener);
        };
    }

    public isPaused(): boolean {
        return this.tradingControl.mode === 'PAUSED';
    }

    public isTradingPaused(): boolean {
        return this.isPaused();
    }

    private isFresh(lastRun: number | null, intervalMs: number): boolean {
        if (!lastRun) return false;
        const now = Date.now();
        const threshold = Math.max(intervalMs * 2, 120_000);
        return (now - lastRun) <= threshold;
    }

    public getSnapshot(): HealthSnapshot {
        return {
            tradingLoopHealthy: this.tradingLoopLastSuccess && this.isFresh(this.tradingLoopLastRun, Math.max(config.POLLING_INTERVAL, 1)),
            tradingLoopLastRun: this.tradingLoopLastRun,
            monitorLoopHealthy: this.isFresh(this.monitorLoopLastRun, Math.max(config.MONITOR_INTERVAL_MS, 1)),
            monitorLoopLastRun: this.monitorLoopLastRun,
            orderSyncHealthy: this.isFresh(this.orderSyncLastRun, Math.max(config.ORDER_SYNC_INTERVAL_MS, 1)),
            orderSyncLastRun: this.orderSyncLastRun,
            lockContentionCount: this.lockContention,
            reconciliationLoopHealthy: this.reconciliationLastSuccess && this.isFresh(this.reconciliationLastRun, Math.max(config.MONITOR_INTERVAL_MS, 1)),
            reconciliationLoopLastRun: this.reconciliationLastRun,
            reconciliationMismatchCount: this.reconciliationMismatchCount,
            reconciliationMissingFromExchange: this.reconciliationMissingFromExchange,
            reconciliationMissingInDb: this.reconciliationMissingInDb,
            reconciliationNoGoTrades: this.reconciliationNoGoTrades,
            reconciliationNoGoReasonCounts: this.reconciliationNoGoReasonCounts,
            reconciliationNoGoSamples: this.reconciliationNoGoSamples,
            reconciliationIntegrityWatchdogTriggered: this.reconciliationIntegrityWatchdogTriggered,
            reconciliationLockContentionCount: this.reconciliationLockContention,
            tradingControl: this.tradingControl
        };
    }

    public recordReconciliationLoop(success: boolean, metrics?: {
        mismatchCount?: number;
        missingFromExchange?: number;
        missingInDb?: number;
        noGoTrades?: number;
        noGoReasonCounts?: Record<string, number>;
        noGoSamples?: ReconciliationNoGoSample[];
        integrityWatchdogTriggered?: boolean;
    }) {
        this.reconciliationLastRun = Date.now();
        this.reconciliationLastSuccess = success;
        if (metrics?.mismatchCount !== undefined) {
            this.reconciliationMismatchCount = metrics.mismatchCount;
        }
        if (metrics?.missingFromExchange !== undefined) {
            this.reconciliationMissingFromExchange = metrics.missingFromExchange;
        }
        if (metrics?.missingInDb !== undefined) {
            this.reconciliationMissingInDb = metrics.missingInDb;
        }
        if (metrics?.noGoTrades !== undefined) {
            this.reconciliationNoGoTrades = metrics.noGoTrades;
        }
        if (metrics?.noGoReasonCounts !== undefined) {
            this.reconciliationNoGoReasonCounts = { ...(metrics.noGoReasonCounts || {}) };
        } else if (this.reconciliationNoGoTrades === 0) {
            this.reconciliationNoGoReasonCounts = {};
        }
        if (metrics?.noGoSamples !== undefined) {
            this.reconciliationNoGoSamples = (metrics.noGoSamples || []).slice(0, 10).map((sample) => ({
                profileId: String(sample?.profileId || '').trim(),
                symbol: String(sample?.symbol || '').trim(),
                tradeId: String(sample?.tradeId || '').trim(),
                reason: String(sample?.reason || '').trim() || 'unknown'
            }));
        } else if (this.reconciliationNoGoTrades === 0) {
            this.reconciliationNoGoSamples = [];
        }
        if (metrics?.integrityWatchdogTriggered !== undefined) {
            this.reconciliationIntegrityWatchdogTriggered = metrics.integrityWatchdogTriggered;
        } else if (this.reconciliationMismatchCount === 0 && this.reconciliationMissingInDb === 0 && this.reconciliationNoGoTrades === 0) {
            this.reconciliationIntegrityWatchdogTriggered = false;
        }
    }
}


export const healthTracker = new HealthTracker();
