<?php

require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/ApiProviderRotationService.php';

class MarketDataProviderService
{
    public static function fetchCandles(string $symbol, string $timeframe, string $interval, int $outputSize = 500): array
    {
        $symbol = strtoupper(trim($symbol));
        $timeframe = strtoupper(trim($timeframe));
        $interval = trim($interval);

        $assetType = self::detectAssetType($symbol);
        $providers = self::getProvidersForAssetType($assetType, 'candles');

        if (!$providers) {
            return [
                'success' => false,
                'provider' => null,
                'error' => 'No active provider available for ' . $assetType . '.',
                'candles' => []
            ];
        }

        $lastError = '';

        foreach ($providers as $provider) {
            $providerName = strtolower(trim((string)($provider['provider'] ?? '')));
            $started = microtime(true);

            try {
                if ($providerName === 'binance') {
                    $result = self::fetchBinanceCandles($provider, $symbol, $timeframe, $interval, $outputSize);
                } elseif ($providerName === 'twelvedata' || $providerName === 'twelve_data') {
                    $result = self::fetchTwelveDataCandles($provider, $symbol, $interval, $outputSize);
                } elseif ($providerName === 'alphavantage' || $providerName === 'alpha_vantage') {
                    $result = self::fetchAlphaVantageCandles($provider, $symbol, $timeframe, $interval, $outputSize);
                } else {
                    $result = [
                        'success' => false,
                        'error' => 'Provider adapter not configured: ' . $providerName
                    ];
                }

                $responseMs = (int)round((microtime(true) - $started) * 1000);

                if (!empty($result['success'])) {
                    ApiProviderRotationService::markSuccess($provider, 'candles', $symbol, $timeframe, 1, $responseMs);

                    return [
                        'success' => true,
                        'provider' => $provider,
                        'candles' => $result['candles'] ?? [],
                        'error' => ''
                    ];
                }

                $lastError = $result['error'] ?? 'Provider failed.';

                if (self::isRateLimitError($lastError)) {
                    ApiProviderRotationService::markRateLimited($provider, 'candles', $lastError, $symbol, $timeframe, $responseMs);
                } else {
                    ApiProviderRotationService::markFailure($provider, 'candles', $lastError, $symbol, $timeframe, $responseMs);
                }

            } catch (Throwable $e) {
                $responseMs = (int)round((microtime(true) - $started) * 1000);
                $lastError = $e->getMessage();
                ApiProviderRotationService::markFailure($provider, 'candles', $lastError, $symbol, $timeframe, $responseMs);
            }
        }

        return [
            'success' => false,
            'provider' => null,
            'error' => $lastError ?: 'All active providers failed.',
            'candles' => []
        ];
    }

    private static function getProvidersForAssetType(string $assetType, string $capability): array
    {
        $providers = ApiProviderRotationService::getAllActiveProviders($capability);

        if (!$providers) {
            return [];
        }

        $filtered = [];

        foreach ($providers as $provider) {
            $name = strtolower(trim((string)($provider['provider'] ?? '')));

            if ($assetType === 'crypto') {
                if ($name === 'binance') {
                    array_unshift($filtered, $provider);
                    continue;
                }

                $filtered[] = $provider;
                continue;
            }

            if ($name === 'binance') {
                continue;
            }

            $filtered[] = $provider;
        }

        return $filtered;
    }

    private static function detectAssetType(string $symbol): string
    {
        $symbol = strtoupper(trim($symbol));

        foreach (['BTC','ETH','SOL','BNB','XRP','DOGE','ADA','AVAX','DOT','TRX','LTC','BCH','MATIC','LINK'] as $crypto) {
            if (strpos($symbol, $crypto) !== false) {
                return 'crypto';
            }
        }

        return 'market';
    }

    private static function fetchBinanceCandles(array $provider, string $symbol, string $timeframe, string $interval, int $outputSize): array
    {
        $binanceSymbol = self::normalizeBinanceSymbol($symbol);
        $binanceInterval = self::mapBinanceInterval($timeframe ?: $interval);

        /*
         * Your server gets HTTP 451 from api.binance.com.
         * So this method forces Binance Vision, even if api_settings still
         * contains the blocked old Binance base_url.
         */
        $baseUrl = 'https://data-api.binance.vision';
        $endpoint = $baseUrl . '/api/v3/klines';

        $target = max(1, min($outputSize, 10000));
        $remaining = $target;
        $endTime = null;
        $allCandles = [];

        while ($remaining > 0) {
            $limit = min($remaining, 1000);

            $params = [
                'symbol' => $binanceSymbol,
                'interval' => $binanceInterval,
                'limit' => $limit
            ];

            if ($endTime !== null) {
                $params['endTime'] = $endTime;
            }

            $response = self::httpGet($endpoint, $params);
            $json = $response['json'];

            if (!is_array($json)) {
                return [
                    'success' => false,
                    'error' => 'Invalid Binance Vision response. HTTP ' . ($response['status_code'] ?? 0) . ' ' . ($response['error'] ?? '')
                ];
            }

            if (isset($json['code']) || isset($json['msg'])) {
                return [
                    'success' => false,
                    'error' => 'Binance Vision error: ' . ($json['msg'] ?? json_encode($json))
                ];
            }

            if (!$json) {
                break;
            }

            $batchCandles = [];

            foreach ($json as $row) {
                if (!is_array($row) || count($row) < 6) {
                    continue;
                }

                $openTime = date('Y-m-d H:i:s', (int)($row[0] / 1000));

                $batchCandles[] = [
                    'datetime' => $openTime,
                    'open_time' => $openTime,
                    'open' => (float)$row[1],
                    'high' => (float)$row[2],
                    'low' => (float)$row[3],
                    'close' => (float)$row[4],
                    'volume' => (float)$row[5]
                ];
            }

            if (!$batchCandles) {
                break;
            }

            $allCandles = array_merge($batchCandles, $allCandles);

            $oldestOpenTimeMs = (int)$json[0][0];
            $endTime = $oldestOpenTimeMs - 1;

            $fetched = count($batchCandles);
            $remaining -= $fetched;

            if ($fetched < $limit) {
                break;
            }

            usleep(150000);
        }

        if (!$allCandles) {
            return [
                'success' => false,
                'error' => 'Binance Vision returned no candles for ' . $binanceSymbol . ' ' . $binanceInterval
            ];
        }

        return [
            'success' => true,
            'candles' => array_slice($allCandles, -$target)
        ];
    }

    private static function normalizeBinanceSymbol(string $symbol): string
    {
        $symbol = strtoupper(trim($symbol));
        $symbol = str_replace([' ', '/', '-'], '', $symbol);

        $map = [
            'BTCUSD' => 'BTCUSDT',
            'BTCUSDT' => 'BTCUSDT',
            'ETHUSD' => 'ETHUSDT',
            'ETHUSDT' => 'ETHUSDT',
            'SOLUSD' => 'SOLUSDT',
            'SOLUSDT' => 'SOLUSDT',
            'BNBUSD' => 'BNBUSDT',
            'BNBUSDT' => 'BNBUSDT',
            'XRPUSD' => 'XRPUSDT',
            'XRPUSDT' => 'XRPUSDT',
            'DOGEUSD' => 'DOGEUSDT',
            'DOGEUSDT' => 'DOGEUSDT',
            'ADAUSD' => 'ADAUSDT',
            'ADAUSDT' => 'ADAUSDT',
        ];

        return $map[$symbol] ?? $symbol;
    }

    private static function mapBinanceInterval(string $timeframe): string
    {
        $raw = trim($timeframe);
        $tf = strtoupper($raw);

        $map = [
            '1M' => '1m', 'M1' => '1m', '1MIN' => '1m', '1MINUTE' => '1m',
            '3M' => '3m', 'M3' => '3m',
            '5M' => '5m', 'M5' => '5m', '5MIN' => '5m',
            '15M' => '15m', 'M15' => '15m', '15MIN' => '15m',
            '30M' => '30m', 'M30' => '30m', '30MIN' => '30m',
            '1H' => '1h', 'H1' => '1h', '1HR' => '1h', '1HOUR' => '1h',
            '2H' => '2h', 'H2' => '2h',
            '4H' => '4h', 'H4' => '4h',
            '6H' => '6h', 'H6' => '6h',
            '8H' => '8h', 'H8' => '8h',
            '12H' => '12h', 'H12' => '12h',
            '1D' => '1d', 'D1' => '1d', 'DAILY' => '1d',
            '3D' => '3d',
            '1W' => '1w', 'W1' => '1w', 'WEEKLY' => '1w',
            'MN' => '1M', '1MN' => '1M', '1MO' => '1M', 'MONTHLY' => '1M',
        ];

        if (isset($map[$tf])) {
            return $map[$tf];
        }

        $allowed = ['1s','1m','3m','5m','15m','30m','1h','2h','4h','6h','8h','12h','1d','3d','1w','1M'];

        if (in_array($raw, $allowed, true)) {
            return $raw;
        }

        return '1h';
    }

    private static function fetchTwelveDataCandles(array $provider, string $symbol, string $interval, int $outputSize): array
    {
        $url = rtrim(!empty($provider['base_url']) ? $provider['base_url'] : 'https://api.twelvedata.com', '/');
        $apiKey = trim((string)($provider['api_key'] ?? ''));

        if ($apiKey === '') {
            return ['success' => false, 'error' => 'TwelveData API key is empty.'];
        }

        $response = self::httpGet($url . '/time_series', [
            'symbol' => $symbol,
            'interval' => $interval,
            'outputsize' => min(max($outputSize, 1), 5000),
            'apikey' => $apiKey
        ]);

        $json = $response['json'];

        if (!empty($json['values']) && is_array($json['values'])) {
            return ['success' => true, 'candles' => array_reverse($json['values'])];
        }

        return [
            'success' => false,
            'error' => $json['message'] ?? $json['status'] ?? $response['error'] ?? 'TwelveData returned no candles.'
        ];
    }

    private static function fetchAlphaVantageCandles(array $provider, string $symbol, string $timeframe, string $interval, int $outputSize): array
    {
        $apiKey = trim((string)($provider['api_key'] ?? ''));

        if ($apiKey === '') {
            return ['success' => false, 'error' => 'AlphaVantage API key is empty.'];
        }

        $response = self::httpGet('https://www.alphavantage.co/query', [
            'function' => 'FX_INTRADAY',
            'from_symbol' => substr($symbol, 0, 3),
            'to_symbol' => substr($symbol, 3, 3),
            'interval' => strtolower($interval),
            'apikey' => $apiKey
        ]);

        $json = $response['json'];

        foreach ($json as $key => $value) {
            if (strpos($key, 'Time Series') !== false && is_array($value)) {
                $candles = [];

                foreach ($value as $time => $row) {
                    $candles[] = [
                        'datetime' => $time,
                        'open_time' => $time,
                        'open' => $row['1. open'] ?? 0,
                        'high' => $row['2. high'] ?? 0,
                        'low' => $row['3. low'] ?? 0,
                        'close' => $row['4. close'] ?? 0,
                        'volume' => 0
                    ];
                }

                return ['success' => true, 'candles' => array_reverse($candles)];
            }
        }

        return [
            'success' => false,
            'error' => $json['Note'] ?? $json['Error Message'] ?? $response['error'] ?? 'AlphaVantage returned no candles.'
        ];
    }

    private static function httpGet(string $url, array $params): array
    {
        $fullUrl = $url . '?' . http_build_query($params);

        if (function_exists('curl_init')) {
            $ch = curl_init($fullUrl);

            curl_setopt_array($ch, [
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_TIMEOUT => 30,
                CURLOPT_CONNECTTIMEOUT => 10,
                CURLOPT_SSL_VERIFYPEER => true,
                CURLOPT_USERAGENT => 'TradingAssistant/1.0'
            ]);

            $body = curl_exec($ch);
            $error = curl_error($ch);
            $statusCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);

            curl_close($ch);
        } else {
            $context = stream_context_create([
                'http' => [
                    'method' => 'GET',
                    'timeout' => 30,
                    'header' => "User-Agent: TradingAssistant/1.0\r\n"
                ]
            ]);

            $body = @file_get_contents($fullUrl, false, $context);
            $error = $body === false ? 'HTTP request failed.' : '';
            $statusCode = 0;
        }

        $json = json_decode((string)$body, true);

        return [
            'json' => is_array($json) ? $json : [],
            'raw' => $body,
            'error' => $error,
            'status_code' => $statusCode ?? 0,
            'url' => $fullUrl
        ];
    }

    private static function isRateLimitError(string $error): bool
    {
        $error = strtolower($error);

        return strpos($error, 'rate') !== false
            || strpos($error, 'limit') !== false
            || strpos($error, 'credits') !== false
            || strpos($error, 'quota') !== false
            || strpos($error, 'too many requests') !== false;
    }
}
