import { config } from '../config/index.js';
import { Indicators } from '../utils/indicators.js';
import { MarketContext, SignalDirection } from '../strategies/rules/types.js';
import logger from '../utils/logger.js';

export interface RiskProfile {
    symbol: string;
    action: SignalDirection;
    positionSize: number;
    stopLoss: number;
    takeProfit: number;
    riskAmount: number;
}

export class RiskEngine {
    public async calculateRiskProfile(
        symbol: string,
        action: SignalDirection,
        context: MarketContext,
        profileOverrides?: any,
        availableCapital?: number
    ): Promise<RiskProfile | null> {
        if (action === SignalDirection.NONE) return null;

        const p = config.PRO_STRATEGY.PARAMETERS;
        const currentPrice = context.currentPrice;

        // 1) Calculate risk amount in USD
        const capital = profileOverrides?.allocated_capital || config.TOTAL_CAPITAL;
        const riskPct = profileOverrides?.risk_per_trade_percent
            ? (profileOverrides.risk_per_trade_percent / 100)
            : p.RISK_PER_TRADE;
        const riskAmount = capital * riskPct;

        // 2) Calculate ATR-based stop-loss distance
        const atr = Indicators.calculateATR(context.candles1h, p.ATR_PERIOD);
        const slMultiplier = p.SL_MULTIPLIER || 1.0;
        let stopLossDistance = 0;

        if (atr === 0) {
            logger.warn(`[RiskEngine] Unable to calculate ATR for ${symbol}. Falling back to default 1% SL.`);
            stopLossDistance = currentPrice * 0.01;
        } else {
            stopLossDistance = atr * slMultiplier;
        }

        const profile = this.buildProfile(
            symbol,
            action,
            currentPrice,
            stopLossDistance,
            riskAmount,
            p.RISK_REWARD_RATIO,
            p.MAX_TP_CAP || 0.01
        );

        // 3) Quantity/notional/precision guardrails
        const executionCfg = profileOverrides?.strategy_config?.execution || {};
        const rawMinQty = Number(executionCfg.minQty ?? config.MIN_POSITION_QTY);
        const rawMaxQty = Number(executionCfg.maxQty ?? config.MAX_POSITION_QTY);
        const rawPrecision = Number(executionCfg.qtyPrecision ?? config.QUANTITY_PRECISION);
        const rawMinNotional = Number(executionCfg.minNotionalUsd ?? config.MIN_NOTIONAL_USD);
        const rawMaxNotional = Number(executionCfg.maxNotionalUsd ?? config.MAX_NOTIONAL_USD);

        const minQty = Number.isFinite(rawMinQty) && rawMinQty > 0 ? rawMinQty : 0.0001;
        const maxQty = Number.isFinite(rawMaxQty) && rawMaxQty > 0 ? rawMaxQty : Number.POSITIVE_INFINITY;
        const qtyPrecision = Number.isFinite(rawPrecision) && rawPrecision >= 0 ? Math.floor(rawPrecision) : 6;
        const minNotional = Number.isFinite(rawMinNotional) && rawMinNotional > 0 ? rawMinNotional : 10;
        const maxNotional = Number.isFinite(rawMaxNotional) && rawMaxNotional > 0 ? rawMaxNotional : Number.POSITIVE_INFINITY;

        profile.positionSize = Math.min(profile.positionSize, maxQty);
        profile.positionSize = this.roundDown(profile.positionSize, qtyPrecision);

        // 4) Available capital guard (with optional reserve)
        const reservePct = Number(config.CAPITAL_RESERVE_PERCENT || 0);
        const reserveCapital = capital * Math.max(0, reservePct) / 100;
        const effectiveAvailableCapital = availableCapital !== undefined
            ? availableCapital
            : Math.max(0, capital - reserveCapital);

        if (effectiveAvailableCapital > 0) {
            const maxAffordableQty = this.roundDown(effectiveAvailableCapital / currentPrice, qtyPrecision);
            if (profile.positionSize > maxAffordableQty) {
                logger.warn(`[RiskEngine] Position exceeds available capital. Scaling qty from ${profile.positionSize} to ${maxAffordableQty}`);
                profile.positionSize = maxAffordableQty;
            }
        }

        // 5) Notional guardrails
        let notional = profile.positionSize * currentPrice;
        if (notional > maxNotional) {
            const maxQtyByNotional = this.roundDown(maxNotional / currentPrice, qtyPrecision);
            logger.warn(`[RiskEngine] Position notional $${notional.toFixed(2)} exceeds max $${maxNotional.toFixed(2)}. Scaling qty to ${maxQtyByNotional}`);
            profile.positionSize = Math.min(profile.positionSize, maxQtyByNotional);
            notional = profile.positionSize * currentPrice;
        }

        if (profile.positionSize < minQty) {
            logger.warn(`[RiskEngine] Qty ${profile.positionSize} below min qty ${minQty} for ${symbol}. Rejecting trade.`);
            return null;
        }

        if (notional < minNotional) {
            logger.warn(`[RiskEngine] Notional $${notional.toFixed(2)} below min $${minNotional.toFixed(2)} for ${symbol}. Rejecting trade.`);
            return null;
        }

        if (profile.positionSize <= 0 || !Number.isFinite(profile.positionSize)) {
            logger.warn(`[RiskEngine] Invalid position size ${profile.positionSize} for ${symbol}. Rejecting trade.`);
            return null;
        }

        // Recompute effective risk after sizing guardrails
        profile.riskAmount = profile.positionSize * stopLossDistance;
        return profile;
    }

    private buildProfile(
        symbol: string,
        action: SignalDirection,
        entry: number,
        slDist: number,
        riskUsd: number,
        rrRatio: number,
        maxTpCap: number
    ): RiskProfile {
        const positionSize = riskUsd / slDist;

        const stopLoss = action === SignalDirection.BUY
            ? entry - slDist
            : entry + slDist;

        const rawTpDist = slDist * rrRatio;
        const maxTpDist = entry * maxTpCap;
        const takeProfitDistance = Math.min(rawTpDist, maxTpDist);

        const takeProfit = action === SignalDirection.BUY
            ? entry + takeProfitDistance
            : entry - takeProfitDistance;

        return {
            symbol,
            action,
            positionSize,
            stopLoss,
            takeProfit,
            riskAmount: riskUsd
        };
    }

    private roundDown(value: number, precision: number): number {
        const safePrecision = Math.max(0, Math.min(10, precision));
        const factor = Math.pow(10, safePrecision);
        return Math.floor(value * factor) / factor;
    }
}
