<?php

require_once __DIR__ . '/SystemConfigService.php';

class RiskService
{
    public static function calculateLotSize(
        float $balance,
        float $riskPercent,
        float $entryPrice,
        float $stopLoss,
        string $symbol = 'XAUUSD',
        float $riskBufferPercent = 10.0
    ): float {
        if ($balance <= 0 || $riskPercent <= 0 || $entryPrice <= 0 || $stopLoss <= 0) {
            return 0;
        }

        $symbol = strtoupper(str_replace(['/', '-', '_', ' '], '', trim($symbol)));
        $riskAmount = $balance * ($riskPercent / 100);
        $priceDifference = abs($entryPrice - $stopLoss);

        if ($priceDifference <= 0) {
            return 0;
        }

        $valuePerOnePriceMovePerLot = self::valuePerOnePriceMovePerLot($symbol, $entryPrice);

        if ($valuePerOnePriceMovePerLot <= 0) {
            return 0;
        }

        $rawLot = $riskAmount / ($priceDifference * $valuePerOnePriceMovePerLot);

        $buffer = max(0, min(50, $riskBufferPercent)) / 100;
        $safeLot = $rawLot * (1 - $buffer);

        if ($safeLot <= 0 || is_nan($safeLot) || is_infinite($safeLot)) {
            return 0;
        }

        $lotStep = self::lotStep($symbol);
        $minLot = self::minLot($symbol);

        $lot = self::floorToStep($safeLot, $lotStep);

        /*
         * Respect the broker minimum.
         * The dashboard can still show estimated risk when min lot causes higher risk.
         */
        if ($lot < $minLot) {
            return self::roundLot($minLot, $lotStep);
        }

        if ($lot > 100) {
            return 100;
        }

        return self::roundLot($lot, $lotStep);
    }

    public static function estimateRiskAmount(
        float $lotSize,
        float $entryPrice,
        float $stopLoss,
        string $symbol = 'XAUUSD'
    ): float {
        if ($lotSize <= 0 || $entryPrice <= 0 || $stopLoss <= 0) {
            return 0;
        }

        $symbol = strtoupper(str_replace(['/', '-', '_', ' '], '', trim($symbol)));
        $distance = abs($entryPrice - $stopLoss);
        $valuePerOnePriceMovePerLot = self::valuePerOnePriceMovePerLot($symbol, $entryPrice);

        return round($distance * $valuePerOnePriceMovePerLot * $lotSize, 2);
    }

    public static function distanceInPips(float $entryPrice, float $stopLoss, string $symbol = 'XAUUSD'): float
    {
        $pipSize = self::pipSize($symbol);

        if ($pipSize <= 0) {
            return 0;
        }

        return abs($entryPrice - $stopLoss) / $pipSize;
    }

    public static function formatPrice($price, string $symbol = 'XAUUSD', string $fallback = 'N/A'): string
    {
        if ($price === null || $price === '' || strtoupper((string)$price) === 'NULL') {
            return $fallback;
        }

        if (!is_numeric($price)) {
            return (string)$price;
        }

        return number_format((float)$price, self::priceDecimals($symbol), '.', '');
    }

    public static function pipSize(string $symbol): float
    {
        $config = self::symbolConfig($symbol);
        $value = (float)($config['pip_size'] ?? 0);

        if ($value > 0) {
            return $value;
        }

        $symbol = strtoupper(str_replace(['/', '-', '_', ' '], '', trim($symbol)));

        if ($symbol === 'XAUUSD') {
            return 0.1;
        }

        if ($symbol === 'XAGUSD') {
            return 0.01;
        }

        if (substr($symbol, -3) === 'JPY') {
            return 0.01;
        }

        if (preg_match('/^[A-Z]{6}$/', $symbol)) {
            return 0.0001;
        }

        return 1.0;
    }

    public static function priceDecimals(string $symbol): int
    {
        $config = self::symbolConfig($symbol);
        $decimals = (int)($config['decimals'] ?? 0);

        if ($decimals > 0) {
            return $decimals;
        }

        $symbol = strtoupper(str_replace(['/', '-', '_', ' '], '', trim($symbol)));

        if ($symbol === 'XAUUSD') {
            return 2;
        }

        if ($symbol === 'XAGUSD') {
            return 3;
        }

        if (substr($symbol, -3) === 'JPY') {
            return 3;
        }

        if (preg_match('/^[A-Z]{6}$/', $symbol)) {
            return 5;
        }

        return 2;
    }

    public static function valuePerOnePriceMovePerLot(string $symbol, float $entryPrice = 0): float
    {
        $config = self::symbolConfig($symbol);
        $contractSize = (float)($config['contract_size'] ?? 0);

        if ($contractSize > 0) {
            return $contractSize;
        }

        /*
         * Fallbacks if metadata has not been upgraded yet.
         */
        $symbol = strtoupper(str_replace(['/', '-', '_', ' '], '', trim($symbol)));

        if ($symbol === 'XAUUSD' || $entryPrice > 1000) {
            return 100;
        }

        if ($symbol === 'XAGUSD') {
            return 5000;
        }

        if (self::isCryptoSymbol($symbol)) {
            return 1;
        }

        if (preg_match('/^[A-Z]{6}$/', $symbol)) {
            return 100000;
        }

        return 1;
    }

    private static function symbolConfig(string $symbol): array
    {
        try {
            if (class_exists('SystemConfigService') && method_exists('SystemConfigService', 'getSymbolConfig')) {
                $config = SystemConfigService::getSymbolConfig($symbol);
                return is_array($config) ? $config : [];
            }
        } catch (Throwable $e) {
        }

        return [];
    }

    public static function lotStepForSymbol(string $symbol): float
    {
        $config = self::symbolConfig($symbol);
        $step = (float)($config['lot_step'] ?? 0);

        if ($step > 0) {
            return $step;
        }

        $symbol = strtoupper(str_replace(['/', '-', '_', ' '], '', trim($symbol)));

        if (self::isCryptoSymbol($symbol)) {
            return strpos($symbol, 'BTC') === 0 ? 0.0001 : 0.001;
        }

        return 0.01;
    }

    public static function minLotForSymbol(string $symbol): float
    {
        $config = self::symbolConfig($symbol);
        $min = (float)($config['min_lot'] ?? 0);

        if ($min > 0) {
            return $min;
        }

        $symbol = strtoupper(str_replace(['/', '-', '_', ' '], '', trim($symbol)));

        if (self::isCryptoSymbol($symbol)) {
            return strpos($symbol, 'BTC') === 0 ? 0.0001 : 0.001;
        }

        return 0.01;
    }

    private static function lotStep(string $symbol): float
    {
        return self::lotStepForSymbol($symbol);
    }

    private static function minLot(string $symbol): float
    {
        return self::minLotForSymbol($symbol);
    }

    private static function floorToStep(float $value, float $step): float
    {
        if ($step <= 0) {
            $step = 0.01;
        }

        return floor($value / $step) * $step;
    }

    public static function lotPrecision(float $step): int
    {
        if ($step <= 0) {
            return 2;
        }

        $stepText = rtrim(rtrim(sprintf('%.8F', $step), '0'), '.');
        $pos = strpos($stepText, '.');

        return $pos === false ? 0 : min(8, strlen($stepText) - $pos - 1);
    }

    public static function formatLot(float $value, float $step = 0.01): string
    {
        if ($value <= 0) {
            return '0';
        }

        $precision = self::lotPrecision($step);
        return number_format($value, $precision, '.', '');
    }

    private static function roundLot(float $value, float $step): float
    {
        return round($value, self::lotPrecision($step));
    }

    private static function isCryptoSymbol(string $symbol): bool
    {
        $symbol = strtoupper(str_replace(['/', '-', '_', ' '], '', trim($symbol)));
        return (bool)preg_match('/^(BTC|ETH|BNB|SOL|XRP|ADA|DOGE|LTC|BCH|AVAX|DOT|MATIC|TRX|LINK|ATOM|NEAR|APT|OP|ARB)/', $symbol);
    }
}
