"""
Market API routes (local-only).
Provides watchlist, market metadata, symbol search, and pricing helpers for the frontend.
"""
from flask import Blueprint, request, jsonify, g
import traceback
import json
import time
from concurrent.futures import ThreadPoolExecutor, as_completed

from app.services.kline import KlineService
from app.utils.logger import get_logger
from app.utils.cache import CacheManager
from app.utils.db import get_db_connection
from app.utils.config_loader import load_addon_config
from app.utils.auth import login_required
from app.data.market_symbols_seed import (
    get_hot_symbols as seed_get_hot_symbols,
    search_symbols as seed_search_symbols,
    get_symbol_name as seed_get_symbol_name
)
from app.services.symbol_name import resolve_symbol_name

logger = get_logger(__name__)

market_bp = Blueprint('market', __name__)
kline_service = KlineService()
cache = CacheManager()

# Thread pool for parallel price fetching
executor = ThreadPoolExecutor(max_workers=10)

def _now_ts() -> int:
    return int(time.time())

def _normalize_symbol(symbol: str) -> str:
    return (symbol or '').strip().upper()

def _ensure_watchlist_table():
    # Table is created by db schema init; this is only a sanity hook.
    return True

@market_bp.route('/config', methods=['GET'])
def get_public_config():
    """
    Public config for frontend (local mode).
    Mirrors the old PHP `/addons/quantdinger/index/getConfig` shape.
    """
    try:
        cfg = load_addon_config()
        models = (cfg.get('ai', {}) or {}).get('models')
        if not isinstance(models, dict) or not models:
            # Fallback defaults (offline friendly)
            models = {
                # Keep some legacy defaults
                'openai/gpt-4o': 'GPT-4o',

                # Unified frontend model list (OpenRouter-style ids)
                'x-ai/grok-code-fast-1': 'xAI: Grok Code Fast 1',
                'x-ai/grok-4-fast': 'xAI: Grok 4 Fast',
                'x-ai/grok-4.1-fast': 'xAI: Grok 4.1 Fast',
                'google/gemini-2.5-flash': 'Google: Gemini 2.5 Flash',
                'google/gemini-2.0-flash-001': 'Google: Gemini 2.0 Flash',
                'google/gemini-3-pro-preview': 'Google: Gemini 3 Pro Preview',
                'google/gemini-2.5-flash-lite': 'Google: Gemini 2.5 Flash Lite',
                'google/gemini-2.5-pro': 'Google: Gemini 2.5 Pro',
                'openai/gpt-4o-mini': 'OpenAI: GPT-4o-mini',
                'openai/gpt-5-mini': 'OpenAI: GPT-5 Mini',
                'openai/gpt-4.1-mini': 'OpenAI: GPT-4.1 Mini',
                'deepseek/deepseek-v3.2': 'DeepSeek: DeepSeek V3.2',
                'minimax/minimax-m2': 'MiniMax: MiniMax M2',
                'anthropic/claude-sonnet-4': 'Anthropic: Claude Sonnet 4',
                'anthropic/claude-sonnet-4.5': 'Anthropic: Claude Sonnet 4.5',
                'anthropic/claude-opus-4.5': 'Anthropic: Claude Opus 4.5',
                'anthropic/claude-haiku-4.5': 'Anthropic: Claude Haiku 4.5',
                'z-ai/glm-4.6': 'Z.AI: GLM 4.6',
            }
        return jsonify({'code': 1, 'msg': 'success', 'data': {'models': models, 'qdt_cost': {}}})
    except Exception as e:
        logger.error(f"get_public_config failed: {str(e)}")
        return jsonify({'code': 0, 'msg': str(e), 'data': None}), 500

@market_bp.route('/types', methods=['GET'])
def get_market_types():
    """Return supported market types for the add-watchlist modal."""
    desired_order = ['USStock', 'Crypto', 'Forex', 'Futures']
    order_rank = {v: i for i, v in enumerate(desired_order)}

    def _normalize_item(x):
        # Expected: {value: 'USStock', i18nKey: '...'}
        if isinstance(x, dict):
            v = (x.get('value') or '').strip()
            if not v:
                return None
            return {
                'value': v,
                'i18nKey': x.get('i18nKey') or f'dashboard.analysis.market.{v}'
            }
        if isinstance(x, str):
            v = x.strip()
            if not v:
                return None
            return {'value': v, 'i18nKey': f'dashboard.analysis.market.{v}'}
        return None

    def _sort_items(items):
        # Keep unknown market types after known ones, stable by original order.
        out = []
        for it in items or []:
            norm = _normalize_item(it)
            if norm:
                out.append(norm)
        out.sort(key=lambda it: (order_rank.get(it['value'], 10_000)))
        return out

    cfg = load_addon_config()
    data = (cfg.get('market', {}) or {}).get('types')

    # Normalize & force desired order (even if config overrides the list order).
    if isinstance(data, list) and data:
        data = _sort_items(data)
    else:
        data = _sort_items(desired_order)
    return jsonify({'code': 1, 'msg': 'success', 'data': data})


@market_bp.route('/menuFooterConfig', methods=['GET'])
def get_menu_footer_config():
    """
    Compatibility stub for old PHP `getMenuFooterConfig`.
    Frontend can also hardcode this locally; this endpoint remains for completeness.
    """
    data = {
        'contact': {
            'support_url': 'https://github.com/',
            'feature_request_url': 'https://github.com/',
            'email': 'support@example.com',
            'live_chat_url': 'https://github.com/'
        },
        'social_accounts': [
            {'name': 'GitHub', 'icon': 'github', 'url': 'https://github.com/'},
            {'name': 'X', 'icon': 'x', 'url': 'https://x.com/'}
        ],
        'legal': {
            'user_agreement': '',
            'privacy_policy': ''
        },
        'copyright': '© 2025-2026 QuantDinger'
    }
    return jsonify({'code': 1, 'msg': 'success', 'data': data})

@market_bp.route('/symbols/search', methods=['GET'])
def search_symbols():
    """
    Lightweight symbol search.
    In local-only mode we keep this simple; frontend allows manual input when no results.
    """
    try:
        market = (request.args.get('market') or '').strip()
        keyword = (request.args.get('keyword') or '').strip().upper()
        limit = int(request.args.get('limit') or 20)

        if not market or not keyword:
            return jsonify({'code': 1, 'msg': 'success', 'data': []})

        out = seed_search_symbols(market=market, keyword=keyword, limit=limit)
        return jsonify({'code': 1, 'msg': 'success', 'data': out})
    except Exception as e:
        logger.error(f"search_symbols failed: {str(e)}")
        logger.error(traceback.format_exc())
        return jsonify({'code': 0, 'msg': str(e), 'data': []}), 500

@market_bp.route('/symbols/hot', methods=['GET'])
def get_hot_symbols():
    """Return a small curated hot list per market (local-only)."""
    try:
        market = (request.args.get('market') or '').strip()
        limit = int(request.args.get('limit') or 10)
        hot = seed_get_hot_symbols(market=market, limit=limit)
        return jsonify({'code': 1, 'msg': 'success', 'data': hot})
    except Exception as e:
        logger.error(f"get_hot_symbols failed: {str(e)}")
        return jsonify({'code': 0, 'msg': str(e), 'data': []}), 500

@market_bp.route('/watchlist/get', methods=['GET'])
@login_required
def get_watchlist():
    """Get watchlist for the current user."""
    try:
        user_id = g.user_id
        _ensure_watchlist_table()
        with get_db_connection() as db:
            cur = db.cursor()
            cur.execute(
                "SELECT id, market, symbol, name FROM qd_watchlist WHERE user_id = ? ORDER BY id DESC",
                (user_id,)
            )
            rows = cur.fetchall() or []

            # Backfill display names for legacy rows (name empty or equals symbol).
            # This keeps UI consistent without requiring users to re-add items.
            for row in rows:
                try:
                    market = row.get('market')
                    symbol = row.get('symbol')
                    current_name = (row.get('name') or '').strip()
                    if not market or not symbol:
                        continue
                    if current_name and current_name != symbol:
                        continue
                    resolved = resolve_symbol_name(market, symbol) or seed_get_symbol_name(market, symbol)
                    if resolved and resolved != current_name:
                        row['name'] = resolved
                        cur.execute(
                            "UPDATE qd_watchlist SET name = ?, updated_at = NOW() WHERE user_id = ? AND market = ? AND symbol = ?",
                            (resolved, user_id, market, symbol)
                        )
                except Exception:
                    continue
            db.commit()
            cur.close()
        return jsonify({'code': 1, 'msg': 'success', 'data': rows})
    except Exception as e:
        logger.error(f"get_watchlist failed: {str(e)}")
        logger.error(traceback.format_exc())
        return jsonify({'code': 0, 'msg': str(e), 'data': []}), 500

@market_bp.route('/watchlist/add', methods=['POST'])
@login_required
def add_watchlist():
    """Add a symbol to watchlist for the current user."""
    try:
        user_id = g.user_id
        data = request.get_json() or {}
        market = (data.get('market') or '').strip()
        symbol = _normalize_symbol(data.get('symbol'))
        name_in = (data.get('name') or '').strip()
        if not market or not symbol:
            return jsonify({'code': 0, 'msg': 'Missing market or symbol', 'data': None}), 400

        # Prefer frontend-provided name (search results), otherwise resolve via seed/public sources.
        resolved = resolve_symbol_name(market, symbol) or seed_get_symbol_name(market, symbol)
        name = name_in or resolved or symbol

        with get_db_connection() as db:
            cur = db.cursor()
            # Insert or update (PostgreSQL UPSERT)
            cur.execute(
                """
                INSERT INTO qd_watchlist (user_id, market, symbol, name, created_at, updated_at) 
                VALUES (?, ?, ?, ?, NOW(), NOW())
                ON CONFLICT(user_id, market, symbol) DO UPDATE SET
                    name = excluded.name,
                    updated_at = NOW()
                """,
                (user_id, market, symbol, name)
            )
            db.commit()
            cur.close()

        return jsonify({'code': 1, 'msg': 'success', 'data': None})
    except Exception as e:
        logger.error(f"add_watchlist failed: {str(e)}")
        logger.error(traceback.format_exc())
        return jsonify({'code': 0, 'msg': str(e), 'data': None}), 500

@market_bp.route('/watchlist/remove', methods=['POST'])
@login_required
def remove_watchlist():
    """Remove a symbol from watchlist for the current user."""
    try:
        user_id = g.user_id
        data = request.get_json() or {}
        symbol = _normalize_symbol(data.get('symbol'))
        if not symbol:
            return jsonify({'code': 0, 'msg': 'Missing symbol', 'data': None}), 400

        with get_db_connection() as db:
            cur = db.cursor()
            cur.execute(
                "DELETE FROM qd_watchlist WHERE user_id = ? AND symbol = ?",
                (user_id, symbol)
            )
            db.commit()
            cur.close()
        return jsonify({'code': 1, 'msg': 'success', 'data': None})
    except Exception as e:
        logger.error(f"remove_watchlist failed: {str(e)}")
        logger.error(traceback.format_exc())
        return jsonify({'code': 0, 'msg': str(e), 'data': None}), 500


def get_single_price(market: str, symbol: str) -> dict:
    """获取单个标的的价格数据"""
    try:
        # 使用 get_realtime_price 获取实时价格（内部已有30秒缓存）
        # 相比原先的 '1D' K线逻辑，这能更及时地反映 Crypto 等 24h 市场的变化
        price_data = kline_service.get_realtime_price(market, symbol)
        
        return {
            'market': market,
            'symbol': symbol,
            'price': price_data.get('price', 0),
            'change': price_data.get('change', 0),
            'changePercent': price_data.get('changePercent', 0)
        }
    except Exception as e:
        logger.error(f"Failed to fetch price {market}:{symbol} - {str(e)}")
        return {
            'market': market,
            'symbol': symbol,
            'price': 0,
            'change': 0,
            'changePercent': 0
        }


@market_bp.route('/watchlist/prices', methods=['GET'])
def get_watchlist_prices():
    """
    批量获取自选股价格
    
    Params (Query String):
        watchlist: JSON string of list of {market, symbol} objects
        e.g. ?watchlist=[{"market":"USStock","symbol":"AAPL"}]
    """
    try:
        watchlist_str = request.args.get('watchlist', '[]')
        try:
            watchlist = json.loads(watchlist_str)
        except Exception:
            watchlist = []
        
        if not watchlist or not isinstance(watchlist, list):
            return jsonify({
                'code': 0,
                'msg': 'Invalid watchlist format (expected JSON list in query param)',
                'data': []
            }), 400
        
        # logger.info(f"开始获取 {len(watchlist)} 个自选股价格数据")
        
        results = []
        
        # 使用线程池并行获取价格
        futures = {}
        for item in watchlist:
            market = item.get('market', '')
            symbol = item.get('symbol', '')
            
            if market and symbol:
                future = executor.submit(get_single_price, market, symbol)
                futures[future] = (market, symbol)
        
        # 收集结果（带超时保护）
        completed_futures = set()
        try:
            for future in as_completed(futures, timeout=30):
                completed_futures.add(future)
                try:
                    result = future.result()
                    results.append(result)
                except Exception as e:
                    market, symbol = futures[future]
                    logger.warning(f"Price fetch failed: {market}:{symbol} - {str(e)}")
                    results.append({
                        'market': market,
                        'symbol': symbol,
                        'price': 0,
                        'change': 0,
                        'changePercent': 0
                    })
        except TimeoutError:
            # 超时时，为未完成的任务添加默认结果
            for future, (market, symbol) in futures.items():
                if future not in completed_futures:
                    logger.warning(f"Price fetch timed out: {market}:{symbol}")
                    results.append({
                        'market': market,
                        'symbol': symbol,
                        'price': 0,
                        'change': 0,
                        'changePercent': 0,
                        'error': 'timeout'
                    })
        
        success_count = sum(1 for r in results if r.get('price', 0) > 0)
        logger.info(f"Watchlist prices: {success_count}/{len(results)} successful")
        
        return jsonify({
            'code': 1,
            'msg': 'success',
            'data': results
        })
        
    except Exception as e:
        logger.error(f"Batch watchlist price fetch failed: {str(e)}")
        logger.error(traceback.format_exc())
        return jsonify({
            'code': 0,
            'msg': f'Failed: {str(e)}',
            'data': []
        }), 500


@market_bp.route('/price', methods=['GET'])
def get_price():
    """
    获取单个标的价格
    
    参数:
        market: 市场类型
        symbol: 交易标的
    """
    try:
        market = request.args.get('market', '')
        symbol = request.args.get('symbol', '')
        
        if not market or not symbol:
            return jsonify({
                'code': 0,
                'msg': 'Missing market or symbol parameter(s)',
                'data': None
            }), 400
        
        result = get_single_price(market, symbol)
        
        return jsonify({
            'code': 1,
            'msg': 'success',
            'data': result
        })
        
    except Exception as e:
        logger.error(f"Failed to fetch price: {str(e)}")
        return jsonify({
            'code': 0,
            'msg': f'Failed: {str(e)}',
            'data': None
        }), 500


@market_bp.route('/stock/name', methods=['POST'])
def get_stock_name():
    """
    获取股票名称
    
    请求体:
    {
        "market": "USStock",
        "symbol": "AAPL"
    }
    
    响应:
    {
        "code": 1,
        "msg": "success",
        "data": {
            "name": "Apple Inc."
        }
    }
    """
    try:
        data = request.get_json()
        if not data:
            return jsonify({
                'code': 0,
                'msg': 'Request body is required',
                'data': None
            }), 400
        
        market = data.get('market', '')
        symbol = data.get('symbol', '')
        
        if not market or not symbol:
            return jsonify({
                'code': 0,
                'msg': 'Missing market or symbol parameter(s)',
                'data': None
            }), 400
        
        # 尝试从缓存获取（1天缓存）
        cache_key = f"stock_name:{market}:{symbol}"
        cached_name = cache.get(cache_key)
        
        if cached_name:
            logger.debug(f"Stock name cache hit: {market}:{symbol}")
            return jsonify({
                'code': 1,
                'msg': 'success',
                'data': {'name': cached_name}
            })
        
        # 根据不同市场获取股票名称
        stock_name = symbol  # 默认使用代码
        
        try:
            if market == 'USStock':
                # 对于股票，尝试获取基本信息
                import yfinance as yf
                
                yf_symbol = symbol
                ticker = yf.Ticker(yf_symbol)
                info = ticker.info
                
                # 尝试获取名称
                stock_name = info.get('longName') or info.get('shortName') or symbol
                
            elif market == 'Crypto':
                # 加密货币，使用交易对格式
                if '/' in symbol:
                    stock_name = symbol
                else:
                    stock_name = f"{symbol}/USDT"
            
            elif market == 'Forex':
                # 外汇
                forex_names = {
                    'XAUUSD': '黄金',
                    'XAGUSD': '白银',
                    'EURUSD': '欧元/美元',
                    'GBPUSD': '英镑/美元',
                    'USDJPY': '美元/日元',
                    'AUDUSD': '澳元/美元',
                    'USDCAD': '美元/加元',
                    'USDCHF': '美元/瑞郎',
                }
                stock_name = forex_names.get(symbol, symbol)
            
            elif market == 'Futures':
                # 期货
                futures_names = {
                    'GC': '黄金期货',
                    'SI': '白银期货',
                    'CL': '原油期货',
                    'NG': '天然气期货',
                    'ZC': '玉米期货',
                    'ZW': '小麦期货',
                    'BTCUSDT': 'BTC永续合约',
                    'ETHUSDT': 'ETH永续合约',
                }
                stock_name = futures_names.get(symbol, symbol)
            
        except Exception as e:
            logger.warning(f"Failed to fetch stock name; falling back to symbol: {market}:{symbol} - {str(e)}")
            stock_name = symbol
        
        # 缓存1天
        cache.set(cache_key, stock_name, 86400)
        
        return jsonify({
            'code': 1,
            'msg': 'success',
            'data': {'name': stock_name}
        })
        
    except Exception as e:
        logger.error(f"Failed to fetch stock name: {str(e)}")
        logger.error(traceback.format_exc())
        return jsonify({
            'code': 0,
            'msg': f'Failed: {str(e)}',
            'data': None
        }), 500
