<?php
/**
 * Finage API Integration for WebTrader
 * Provides real-time market data from Finage.co.uk
 * Supports both Forex and Stock data with WebSocket integration
 */

class FinageAPI {
    private $apiKey;
    private $baseUrl = 'https://api.finage.co.uk';
    private $wsUrl = 'wss://w1.finage.co.uk/websocket/';
    private $config;
    
    public function __construct($apiKey) {
        $this->apiKey = $apiKey;
        // Cargar configuración desde simple_crm/config (dos niveles arriba)
        // Nota: __DIR__ aquí es simple_crm/modules/webtrader
        $configPath = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'finage_config.php';
        if (file_exists($configPath)) {
            $this->config = include($configPath);
        } else {
            $this->config = [];
        }
        // Permitir sobreescribir URLs desde configuración
        if (!empty($this->config['base_url'])) {
            $this->baseUrl = $this->config['base_url'];
        }
        if (!empty($this->config['websocket_url'])) {
            $this->wsUrl = $this->config['websocket_url'];
        }
    }
    
    /**
     * Test API connection
     */
    public function testConnection() {
        $url = $this->baseUrl . "/last/forex/EURUSD?apikey={$this->apiKey}";
        
        $response = $this->makeRequest($url);
        
        if ($response && isset($response['bid'])) {
            return [
                'success' => true,
                'message' => 'Conexión exitosa a Finage API',
                'data' => $response
            ];
        }
        
        return [
            'success' => false,
            'message' => 'Error conectando a Finage API',
            'error' => 'Invalid API response'
        ];
    }
    
    /**
     * Get real-time price for a forex symbol
     */
    public function getRealTimePrice($symbol) {
        // Detectar si es crypto y utilizar endpoint correspondiente
        $isCrypto = in_array($symbol, ($this->config['crypto_symbols'] ?? []));
        $endpoint = $isCrypto ? "/last/crypto/{$symbol}" : "/last/forex/{$symbol}";
        $url = $this->baseUrl . $endpoint . "?apikey={$this->apiKey}";
        
        $response = $this->makeRequest($url);
        
        if ($response && isset($response['bid']) && isset($response['ask'])) {
            return [
                'symbol' => $symbol,
                'bid' => floatval($response['bid']),
                'ask' => floatval($response['ask']),
                'spread' => floatval($response['ask']) - floatval($response['bid']),
                'timestamp' => time(),
                'change' => $response['change'] ?? 0,
                'change_percent' => $response['changepct'] ?? 0,
                'type' => $isCrypto ? 'crypto' : 'forex'
            ];
        }

        return null;
    }
    
    /**
     * Get stock snapshot data
     */
    public function getStockSnapshot($symbols = null) {
        if (!$symbols) {
            $symbols = implode(',', $this->config['stock_symbols']);
        } else if (is_array($symbols)) {
            $symbols = implode(',', $symbols);
        }
        
        $url = $this->baseUrl . "/snapshot/stock?quotes=true&trades=true&symbols={$symbols}&apikey={$this->apiKey}";
        
        $response = $this->makeRequest($url);
        
        if ($response && isset($response['lastQuotes'])) {
            $stockData = [];
            
            foreach ($response['lastQuotes'] as $quote) {
                $stockData[$quote['s']] = [
                    'symbol' => $quote['s'],
                    'bid' => floatval($quote['b']),
                    'ask' => floatval($quote['a']),
                    'spread' => floatval($quote['a']) - floatval($quote['b']),
                    'timestamp' => intval($quote['t']),
                    'bid_size' => intval($quote['bsz'] ?? 0),
                    'ask_size' => intval($quote['asz'] ?? 0),
                    'type' => 'stock'
                ];
            }
            
            // Add trade data if available
            if (isset($response['lastTrades'])) {
                foreach ($response['lastTrades'] as $trade) {
                    if (isset($stockData[$trade['s']])) {
                        $stockData[$trade['s']]['last_price'] = floatval($trade['p']);
                        $stockData[$trade['s']]['last_size'] = intval($trade['sz']);
                        $stockData[$trade['s']]['last_timestamp'] = intval($trade['t']);
                    }
                }
            }
            
            return $stockData;
        }
        
        return [];
    }
    
    /**
     * Get multiple symbols prices
     */
    public function getMultiplePrices($symbols) {
        $prices = [];
        
        foreach ($symbols as $symbol) {
            $price = $this->getRealTimePrice($symbol);
            if ($price) {
                $prices[$symbol] = $price;
            }
        }
        
        return $prices;
    }
    
    /**
     * Get historical data for charts (supports both forex and stocks)
     */
    public function getHistoricalData($symbol, $period = '1day', $limit = 100, $type = 'forex') {
        $endpoint = $type === 'stock' ? 'stock' : 'forex';
        $url = $this->baseUrl . "/agg/{$endpoint}/{$symbol}/{$period}?limit={$limit}&apikey={$this->apiKey}";
        
        $response = $this->makeRequest($url);
        
        if ($response && isset($response['results'])) {
            $chartData = [];
            
            foreach ($response['results'] as $candle) {
                $chartData[] = [
                    'time' => $candle['t'] / 1000, // Convert to seconds
                    'open' => floatval($candle['o']),
                    'high' => floatval($candle['h']),
                    'low' => floatval($candle['l']),
                    'close' => floatval($candle['c']),
                    'volume' => intval($candle['v'] ?? 0)
                ];
            }
            
            return $chartData;
        }
        
        return [];
    }
    
    /**
     * Get intraday historical data for detailed charts
     */
    public function getIntradayData($symbol, $interval = '1min', $limit = 100, $type = 'forex') {
        $endpoint = $type === 'stock' ? 'stock' : 'forex';
        $url = $this->baseUrl . "/agg/{$endpoint}/{$symbol}/{$interval}?limit={$limit}&apikey={$this->apiKey}";
        
        $response = $this->makeRequest($url);
        
        if ($response && isset($response['results'])) {
            $chartData = [];
            
            foreach ($response['results'] as $candle) {
                $chartData[] = [
                    'time' => $candle['t'] / 1000,
                    'open' => floatval($candle['o']),
                    'high' => floatval($candle['h']),
                    'low' => floatval($candle['l']),
                    'close' => floatval($candle['c']),
                    'volume' => intval($candle['v'] ?? 0)
                ];
            }
            
            return $chartData;
        }
        
        return [];
    }

    /**
     * Get aggregates for a specific date range using Finage schema:
     * /agg/{market}/{symbol}/{multiply}/{time}/{from}/{to}
     * Example: /agg/forex/EURUSD/5/minute/2025-11-01/2025-11-10
     */
    public function getAggregatesRange($symbol, $multiply = 1, $time = 'minute', $from = null, $to = null, $type = 'forex') {
        $endpoint = $type === 'stock' ? 'stock' : ($type === 'crypto' ? 'crypto' : 'forex');
        // Validate basics
        $m = max(1, intval($multiply));
        $unit = in_array($time, ['minute','hour','day','week','month','quarter','year']) ? $time : 'minute';
        if (!$from || !$to) { return []; }
        // Build URL per Finage docs
        $url = $this->baseUrl . "/agg/{$endpoint}/{$symbol}/{$m}/{$unit}/{$from}/{$to}?apikey={$this->apiKey}";
        $response = $this->makeRequest($url);
        if ($response && isset($response['results'])) {
            $chartData = [];
            foreach ($response['results'] as $candle) {
                $chartData[] = [
                    'time' => $candle['t'] / 1000,
                    'open' => floatval($candle['o']),
                    'high' => floatval($candle['h']),
                    'low' => floatval($candle['l']),
                    'close' => floatval($candle['c']),
                    'volume' => intval($candle['v'] ?? 0)
                ];
            }
            return $chartData;
        }
        return [];
    }
    
    /**
     * Get market status
     */
    public function getMarketStatus() {
        // Intentar endpoints conocidos; si fallan (406/404), usar cálculo local
        $endpoint = $this->config['endpoints']['market_status'] ?? '/market-status/forex';
        $urlsToTry = [
            $this->baseUrl . $endpoint . "?apikey={$this->apiKey}",
            $this->baseUrl . "/market-status?market=forex&apikey={$this->apiKey}"
        ];
        foreach ($urlsToTry as $url) {
            $response = $this->makeRequest($url);
            if (is_array($response) && (isset($response['status']) || isset($response['is_open']))) {
                return [
                    'is_open' => $response['is_open'] ?? ($response['status'] === 'open'),
                    'market_status' => $response['status'] ?? 'open',
                    'next_open' => $response['next_open'] ?? null,
                    'next_close' => $response['next_close'] ?? null
                ];
            }
        }
        // Fallback: calcular estado del mercado FOREX en UTC
        return $this->computeForexMarketStatusUTC();
    }
    
    /**
     * Get available symbols
     */
    public function getAvailableSymbols() {
        $url = $this->baseUrl . "/symbols/forex?apikey={$this->apiKey}";
        
        $response = $this->makeRequest($url);
        
        if ($response && isset($response['symbols'])) {
            return $response['symbols'];
        }
        
        // Return default symbols if API fails
        return [
            ['symbol' => 'EURUSD', 'name' => 'Euro / US Dollar'],
            ['symbol' => 'GBPUSD', 'name' => 'British Pound / US Dollar'],
            ['symbol' => 'USDJPY', 'name' => 'US Dollar / Japanese Yen'],
            ['symbol' => 'USDCHF', 'name' => 'US Dollar / Swiss Franc'],
            ['symbol' => 'AUDUSD', 'name' => 'Australian Dollar / US Dollar'],
            ['symbol' => 'USDCAD', 'name' => 'US Dollar / Canadian Dollar'],
            ['symbol' => 'NZDUSD', 'name' => 'New Zealand Dollar / US Dollar'],
            ['symbol' => 'EURJPY', 'name' => 'Euro / Japanese Yen']
        ];
    }
    
    /**
     * Generate WebSocket authentication token with support for stocks
     */
    public function getWebSocketToken($includeStocks = true, $includeCrypto = true) {
        // Backward-compatible single-URL config
        $symbols = $this->config['default_symbols'] ?? [];
        if ($includeStocks && isset($this->config['stock_symbols'])) {
            $symbols = array_merge($symbols, $this->config['stock_symbols']);
        }
        if ($includeCrypto && isset($this->config['crypto_symbols'])) {
            $symbols = array_merge($symbols, $this->config['crypto_symbols']);
        }
        return [
            'url' => $this->wsUrl,
            'token' => ($this->config['socket_key'] ?? $this->apiKey),
            'symbols' => $symbols
        ];
    }

    /**
     * Build category-specific WebSocket subscriptions using dedicated servers
     */
    public function getWebSocketSubscriptions($includeForex = true, $includeStocks = true, $includeCrypto = true, $includeIndices = true, $includeEtfs = true) {
        $servers = $this->config['ws_servers'] ?? [];
        $socketKey = $this->config['socket_key'] ?? $this->apiKey;
        $subs = [];

        if ($includeForex && !empty($servers['forex'])) {
            $subs[] = [
                'url' => $servers['forex'],
                'token' => $socketKey,
                'symbols' => $this->config['default_symbols'] ?? []
            ];
        }
        if ($includeStocks && !empty($servers['stocks'])) {
            $subs[] = [
                'url' => $servers['stocks'],
                'token' => $socketKey,
                'symbols' => $this->config['stock_symbols'] ?? []
            ];
        }
        if ($includeCrypto && !empty($servers['crypto'])) {
            $subs[] = [
                'url' => $servers['crypto'],
                'token' => $socketKey,
                'symbols' => $this->config['crypto_symbols'] ?? []
            ];
        }
        if ($includeIndices && !empty($servers['indices'])) {
            $subs[] = [
                'url' => $servers['indices'],
                'token' => $socketKey,
                'symbols' => $this->config['indices_symbols'] ?? []
            ];
        }
        if ($includeEtfs && !empty($servers['etf'])) {
            $subs[] = [
                'url' => $servers['etf'],
                'token' => $socketKey,
                'symbols' => $this->config['etf_symbols'] ?? []
            ];
        }

        // Fallback to single URL if servers missing
        if (empty($subs)) {
            $single = $this->getWebSocketToken(true, true);
            $subs[] = $single;
        }
        return $subs;
    }
    
    /**
     * Get combined market data (forex + stocks)
     */
    public function getCombinedMarketData() {
        $forexData = $this->getMultiplePrices($this->config['default_symbols']);
        $stockData = $this->getStockSnapshot();
        $cryptoData = [];
        if (!empty($this->config['crypto_symbols'])) {
            foreach ($this->config['crypto_symbols'] as $sym) {
                $p = $this->getRealTimePrice($sym);
                if ($p) $cryptoData[$sym] = $p;
            }
        }
        
        return [
            'forex' => $forexData,
            'stocks' => $stockData,
            'crypto' => $cryptoData,
            'timestamp' => time(),
            'market_status' => $this->getMarketStatus()
        ];
    }
    
    /**
     * Make HTTP request to Finage API
     */
    private function makeRequest($url) {
        $ch = curl_init();
        
        curl_setopt_array($ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 10,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_USERAGENT => 'WebTrader/1.0',
            CURLOPT_HTTPHEADER => ['Accept: application/json']
        ]);
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        
        curl_close($ch);
        
        if ($httpCode === 200 && $response) {
            return json_decode($response, true);
        }
        
        // Log error for debugging
        error_log("Finage API Error: HTTP {$httpCode} - {$url}");
        
        return null;
    }

    /**
     * Calcular estado de mercado FOREX en UTC como respaldo
     */
    private function computeForexMarketStatusUTC() {
        $now = new \DateTime('now', new \DateTimeZone('UTC'));
        $dayOfWeek = (int)$now->format('N'); // 1=Lunes, 7=Domingo
        $hour = (int)$now->format('H');
        $minute = (int)$now->format('i');
        $minutes = $hour * 60 + $minute;
        // Cerrado fines de semana
        if ($dayOfWeek === 6 || $dayOfWeek === 7) {
            return [
                'is_open' => false,
                'market_status' => 'closed',
                'next_open' => $this->nextForexOpenUTC($now),
                'next_close' => null
            ];
        }
        // Sesiones principales (UTC)
        $sessions = [
            ['start' => 22*60, 'end' => 7*60],   // Sydney
            ['start' => 0, 'end' => 9*60],       // Tokyo
            ['start' => 8*60, 'end' => 17*60],   // London
            ['start' => 13*60, 'end' => 22*60],  // New York
        ];
        $open = false;
        foreach ($sessions as $s) {
            $start = $s['start'];
            $end = $s['end'];
            if ($start > $end) {
                if ($minutes >= $start || $minutes <= $end) { $open = true; break; }
            } else {
                if ($minutes >= $start && $minutes <= $end) { $open = true; break; }
            }
        }
        return [
            'is_open' => $open,
            'market_status' => $open ? 'open' : 'closed',
            'next_open' => $open ? null : $this->nextForexOpenUTC($now),
            'next_close' => $open ? $this->nextForexCloseUTC($now) : null
        ];
    }

    private function nextForexOpenUTC(\DateTime $now) {
        // Próxima apertura: Lunes 22:00 UTC (Sydney) si fin de semana; si días hábiles, siguiente sesión que comience
        $day = (int)$now->format('N');
        $dt = clone $now;
        if ($day === 6) { // Sábado -> Lunes
            $dt->modify('next monday')->setTime(22, 0);
        } elseif ($day === 7) { // Domingo -> Lunes
            $dt->modify('next monday')->setTime(22, 0);
        } else {
            // Buscar siguiente inicio de sesión de hoy
            $mins = (int)$now->format('H')*60 + (int)$now->format('i');
            $starts = [0, 8*60, 13*60, 22*60];
            foreach ($starts as $st) {
                if ($mins < $st) { $dt->setTime(intval($st/60), $st%60); return $dt->getTimestamp(); }
            }
            // Si no hay más hoy, siguiente día 00:00
            $dt->modify('+1 day')->setTime(0,0);
        }
        return $dt->getTimestamp();
    }

    private function nextForexCloseUTC(\DateTime $now) {
        // Próximo cierre: fin de sesión actual más cercana
        $mins = (int)$now->format('H')*60 + (int)$now->format('i');
        $ends = [7*60, 9*60, 17*60, 22*60];
        $dt = clone $now;
        foreach ($ends as $en) {
            if ($mins <= $en) { $dt->setTime(intval($en/60), $en%60); return $dt->getTimestamp(); }
        }
        // Si pasó todas, cierre a 22:00
        $dt->setTime(22,0);
        return $dt->getTimestamp();
    }
}

/**
 * Get Finage API instance with configuration
 */
function getFinageAPI() {
    $apiKey = null;

    // Cargar desde archivo de configuración primero (ruta relativa al módulo)
    $configPath = realpath(__DIR__ . '/../../config/finage_config.php');
    if ($configPath && file_exists($configPath)) {
        if (function_exists('opcache_invalidate')) { @opcache_invalidate($configPath, true); }
        $config = include $configPath;
        if (is_array($config) && isset($config['api_key']) && trim($config['api_key']) !== '') {
            $apiKey = $config['api_key'];
        }
    }

    // Si no hay clave en config, intentar variable de entorno
    if (!$apiKey && isset($_ENV['FINAGE_API_KEY']) && trim($_ENV['FINAGE_API_KEY']) !== '') {
        $apiKey = $_ENV['FINAGE_API_KEY'];
    }

    // Validación final: no usar placeholder
    if (!$apiKey || $apiKey === 'YOUR_FINAGE_API_KEY_HERE') {
        error_log('ERROR: Finage API key no configurada. Configure "api_key" en config/finage_config.php o variable de entorno FINAGE_API_KEY.');
    }

    return new FinageAPI($apiKey);
}
?>
