"""
TradingAgents免费配置模块

该模块提供免费数据源的配置管理，用于替代昂贵的付费数据源配置。
支持环境变量配置、配置验证和动态配置更新。

@author TradingAgents团队
@version MVP-1.0
@since 2024-12
"""

import os
import json
import logging
from typing import Dict, Any, Optional, List
from dataclasses import dataclass, asdict
from pathlib import Path

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


@dataclass
class FreeDataSourcesConfig:
    """
    免费数据源配置类
    
    @description 统一管理所有免费数据源的配置参数
    @version MVP-1.0
    """
    
    # Alpha Vantage配置 (免费层: 500次/日)
    alpha_vantage_api_key: Optional[str] = None
    alpha_vantage_max_calls_per_day: int = 400  # 保留100次缓冲
    alpha_vantage_calls_per_minute: int = 5
    
    # SEC Edgar配置 (完全免费)
    sec_edgar_user_agent: str = "TradingAgents-MVP/1.0 (contact@tradingagents.ai)"
    sec_edgar_rate_limit: float = 10.0  # 每秒最多10次请求
    
    # Yahoo Finance配置 (免费但有限制)
    yahoo_finance_timeout: int = 30
    yahoo_finance_retry_count: int = 3
    yahoo_finance_retry_delay: float = 1.0
    
    # 缓存配置
    cache_enabled: bool = True
    cache_directory: str = "data/cache"
    cache_ttl_hours: int = 6
    cache_max_size_mb: int = 500
    
    # 新闻聚合配置
    news_sources_enabled: List[str] = None
    news_max_articles_per_source: int = 10
    news_cache_hours: int = 2
    
    # 回退和容错配置
    enable_fallback_sources: bool = True
    max_retry_attempts: int = 3
    request_timeout: int = 30
    
    # 日志和监控配置
    log_level: str = "INFO"
    enable_performance_monitoring: bool = True
    enable_cost_tracking: bool = True
    
    def __post_init__(self):
        """初始化后处理"""
        if self.news_sources_enabled is None:
            self.news_sources_enabled = ["yahoo_finance", "google_news"]
        
        # 从环境变量加载配置
        self._load_from_env()
        
        # 验证配置
        self._validate_config()
    
    def _load_from_env(self):
        """从环境变量加载配置"""
        # Alpha Vantage
        if os.getenv("ALPHA_VANTAGE_API_KEY"):
            self.alpha_vantage_api_key = os.getenv("ALPHA_VANTAGE_API_KEY")
        
        # 缓存配置
        if os.getenv("TRADING_CACHE_DIR"):
            self.cache_directory = os.getenv("TRADING_CACHE_DIR")
        
        if os.getenv("TRADING_CACHE_TTL_HOURS"):
            try:
                self.cache_ttl_hours = int(os.getenv("TRADING_CACHE_TTL_HOURS"))
            except ValueError:
                logger.warning("无效的TRADING_CACHE_TTL_HOURS值，使用默认值")
        
        # 日志级别
        if os.getenv("TRADING_LOG_LEVEL"):
            self.log_level = os.getenv("TRADING_LOG_LEVEL").upper()
        
        # 超时配置
        if os.getenv("TRADING_REQUEST_TIMEOUT"):
            try:
                self.request_timeout = int(os.getenv("TRADING_REQUEST_TIMEOUT"))
            except ValueError:
                logger.warning("无效的TRADING_REQUEST_TIMEOUT值，使用默认值")
    
    def _validate_config(self):
        """验证配置参数"""
        errors = []
        
        # 验证Alpha Vantage配置
        if self.alpha_vantage_max_calls_per_day <= 0:
            errors.append("alpha_vantage_max_calls_per_day必须大于0")
        
        if self.alpha_vantage_calls_per_minute <= 0:
            errors.append("alpha_vantage_calls_per_minute必须大于0")
        
        # 验证缓存配置
        if self.cache_ttl_hours <= 0:
            errors.append("cache_ttl_hours必须大于0")
        
        if self.cache_max_size_mb <= 0:
            errors.append("cache_max_size_mb必须大于0")
        
        # 验证超时配置
        if self.request_timeout <= 0:
            errors.append("request_timeout必须大于0")
        
        # 验证日志级别
        valid_log_levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
        if self.log_level not in valid_log_levels:
            errors.append(f"log_level必须是以下之一: {valid_log_levels}")
        
        if errors:
            raise ValueError(f"配置验证失败: {'; '.join(errors)}")
        
        logger.info("配置验证通过")
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典格式"""
        return asdict(self)
    
    def save_to_file(self, file_path: str):
        """保存配置到文件"""
        try:
            config_dict = self.to_dict()
            # 不保存敏感信息到文件
            config_dict.pop("alpha_vantage_api_key", None)
            
            Path(file_path).parent.mkdir(parents=True, exist_ok=True)
            
            with open(file_path, 'w', encoding='utf-8') as f:
                json.dump(config_dict, f, indent=2, ensure_ascii=False)
            
            logger.info(f"配置已保存到: {file_path}")
            
        except Exception as e:
            logger.error(f"保存配置失败: {e}")
            raise
    
    @classmethod
    def load_from_file(cls, file_path: str) -> 'FreeDataSourcesConfig':
        """从文件加载配置"""
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                config_dict = json.load(f)
            
            # 从环境变量补充敏感信息
            if not config_dict.get("alpha_vantage_api_key"):
                config_dict["alpha_vantage_api_key"] = os.getenv("ALPHA_VANTAGE_API_KEY")
            
            logger.info(f"配置已从文件加载: {file_path}")
            return cls(**config_dict)
            
        except FileNotFoundError:
            logger.warning(f"配置文件不存在: {file_path}，使用默认配置")
            return cls()
        except Exception as e:
            logger.error(f"加载配置失败: {e}")
            raise


@dataclass
class TradingAgentsConfig:
    """
    TradingAgents主配置类
    
    @description 整合所有配置模块，提供统一的配置管理接口
    @version MVP-1.0
    """
    
    # 数据源配置
    data_sources: FreeDataSourcesConfig = None
    
    # LLM配置
    openai_api_key: Optional[str] = None
    openai_model: str = "gpt-4"
    openai_temperature: float = 0.1
    openai_max_tokens: int = 2000
    
    # Reddit API配置 (用于社交情感分析)
    reddit_client_id: Optional[str] = None
    reddit_client_secret: Optional[str] = None
    reddit_user_agent: str = "TradingAgents-MVP/1.0"
    
    # 智能体配置
    max_analysis_depth: int = 3
    confidence_threshold: float = 0.7
    risk_tolerance: str = "moderate"  # conservative, moderate, aggressive
    
    # 系统配置
    debug_mode: bool = False
    enable_web_interface: bool = True
    web_port: int = 8501  # Streamlit默认端口
    
    # 性能配置
    max_concurrent_requests: int = 5
    request_pool_size: int = 10
    
    def __post_init__(self):
        """初始化后处理"""
        if self.data_sources is None:
            self.data_sources = FreeDataSourcesConfig()
        
        # 从环境变量加载配置
        self._load_from_env()
        
        # 验证配置
        self._validate_config()
    
    def _load_from_env(self):
        """从环境变量加载配置"""
        # OpenAI配置
        if os.getenv("OPENAI_API_KEY"):
            self.openai_api_key = os.getenv("OPENAI_API_KEY")
        
        if os.getenv("OPENAI_MODEL"):
            self.openai_model = os.getenv("OPENAI_MODEL")
        
        # Reddit配置
        if os.getenv("REDDIT_CLIENT_ID"):
            self.reddit_client_id = os.getenv("REDDIT_CLIENT_ID")
        
        if os.getenv("REDDIT_CLIENT_SECRET"):
            self.reddit_client_secret = os.getenv("REDDIT_CLIENT_SECRET")
        
        # 系统配置
        if os.getenv("TRADING_DEBUG_MODE"):
            self.debug_mode = os.getenv("TRADING_DEBUG_MODE").lower() == "true"
        
        if os.getenv("TRADING_WEB_PORT"):
            try:
                self.web_port = int(os.getenv("TRADING_WEB_PORT"))
            except ValueError:
                logger.warning("无效的TRADING_WEB_PORT值，使用默认值")
        
        # 风险配置
        if os.getenv("TRADING_RISK_TOLERANCE"):
            risk_tolerance = os.getenv("TRADING_RISK_TOLERANCE").lower()
            if risk_tolerance in ["conservative", "moderate", "aggressive"]:
                self.risk_tolerance = risk_tolerance
    
    def _validate_config(self):
        """验证配置参数"""
        errors = []
        
        # 验证必需的API密钥
        if not self.openai_api_key:
            errors.append("OpenAI API密钥未设置 (OPENAI_API_KEY)")
        
        # 验证LLM配置
        if self.openai_temperature < 0 or self.openai_temperature > 2:
            errors.append("openai_temperature必须在0-2之间")
        
        if self.openai_max_tokens <= 0:
            errors.append("openai_max_tokens必须大于0")
        
        # 验证智能体配置
        if self.max_analysis_depth <= 0:
            errors.append("max_analysis_depth必须大于0")
        
        if self.confidence_threshold < 0 or self.confidence_threshold > 1:
            errors.append("confidence_threshold必须在0-1之间")
        
        valid_risk_levels = ["conservative", "moderate", "aggressive"]
        if self.risk_tolerance not in valid_risk_levels:
            errors.append(f"risk_tolerance必须是以下之一: {valid_risk_levels}")
        
        # 验证性能配置
        if self.max_concurrent_requests <= 0:
            errors.append("max_concurrent_requests必须大于0")
        
        if self.request_pool_size <= 0:
            errors.append("request_pool_size必须大于0")
        
        # 验证Web配置
        if self.web_port < 1024 or self.web_port > 65535:
            errors.append("web_port必须在1024-65535之间")
        
        if errors:
            raise ValueError(f"配置验证失败: {'; '.join(errors)}")
        
        logger.info("主配置验证通过")
    
    def get_data_source_config(self) -> Dict[str, Any]:
        """获取数据源配置字典"""
        return self.data_sources.to_dict()
    
    def get_llm_config(self) -> Dict[str, Any]:
        """获取LLM配置字典"""
        return {
            "api_key": self.openai_api_key,
            "model": self.openai_model,
            "temperature": self.openai_temperature,
            "max_tokens": self.openai_max_tokens
        }
    
    def get_reddit_config(self) -> Dict[str, Any]:
        """获取Reddit配置字典"""
        return {
            "client_id": self.reddit_client_id,
            "client_secret": self.reddit_client_secret,
            "user_agent": self.reddit_user_agent
        }
    
    def is_production_ready(self) -> bool:
        """检查是否已准备好生产环境"""
        required_keys = [
            self.openai_api_key,
            self.data_sources.alpha_vantage_api_key
        ]
        
        missing_keys = [key for key in required_keys if not key]
        
        if missing_keys:
            logger.warning(f"生产环境检查失败，缺少必需的API密钥: {len(missing_keys)}个")
            return False
        
        logger.info("生产环境检查通过")
        return True
    
    def get_cost_estimate(self) -> Dict[str, Any]:
        """获取成本估算"""
        return {
            "monthly_cost_usd": 30.0,  # 预估月成本
            "cost_breakdown": {
                "openai_api": 25.0,  # OpenAI API成本
                "alpha_vantage": 0.0,  # 免费层
                "sec_edgar": 0.0,  # 完全免费
                "yahoo_finance": 0.0,  # 免费
                "infrastructure": 5.0  # 服务器/云服务成本
            },
            "cost_savings_vs_finnhub": {
                "previous_monthly_cost": 197.0,
                "current_monthly_cost": 30.0,
                "monthly_savings": 167.0,
                "savings_percentage": 84.8
            }
        }


class ConfigManager:
    """
    配置管理器
    
    @description 提供配置的加载、保存、验证和动态更新功能
    @version MVP-1.0
    """
    
    def __init__(self, config_dir: str = "config"):
        """
        初始化配置管理器
        
        @param {str} config_dir - 配置文件目录
        """
        self.config_dir = Path(config_dir)
        self.config_dir.mkdir(parents=True, exist_ok=True)
        
        self.main_config_file = self.config_dir / "trading_config.json"
        self.data_sources_config_file = self.config_dir / "data_sources_config.json"
        
        self._config: Optional[TradingAgentsConfig] = None
    
    def load_config(self) -> TradingAgentsConfig:
        """
        加载配置
        
        @returns {TradingAgentsConfig} 加载的配置对象
        """
        try:
            # 首先尝试加载数据源配置
            if self.data_sources_config_file.exists():
                data_sources_config = FreeDataSourcesConfig.load_from_file(
                    str(self.data_sources_config_file)
                )
            else:
                data_sources_config = FreeDataSourcesConfig()
            
            # 然后加载主配置
            if self.main_config_file.exists():
                with open(self.main_config_file, 'r', encoding='utf-8') as f:
                    main_config_dict = json.load(f)
                
                # 移除数据源配置，因为我们单独加载
                main_config_dict.pop("data_sources", None)
                
                config = TradingAgentsConfig(
                    data_sources=data_sources_config,
                    **main_config_dict
                )
            else:
                config = TradingAgentsConfig(data_sources=data_sources_config)
            
            self._config = config
            logger.info("配置加载成功")
            return config
            
        except Exception as e:
            logger.error(f"配置加载失败: {e}")
            # 返回默认配置
            self._config = TradingAgentsConfig()
            return self._config
    
    def save_config(self, config: TradingAgentsConfig):
        """
        保存配置
        
        @param {TradingAgentsConfig} config - 要保存的配置对象
        """
        try:
            # 保存数据源配置
            config.data_sources.save_to_file(str(self.data_sources_config_file))
            
            # 保存主配置 (不包含数据源配置和敏感信息)
            main_config_dict = {
                "openai_model": config.openai_model,
                "openai_temperature": config.openai_temperature,
                "openai_max_tokens": config.openai_max_tokens,
                "reddit_user_agent": config.reddit_user_agent,
                "max_analysis_depth": config.max_analysis_depth,
                "confidence_threshold": config.confidence_threshold,
                "risk_tolerance": config.risk_tolerance,
                "debug_mode": config.debug_mode,
                "enable_web_interface": config.enable_web_interface,
                "web_port": config.web_port,
                "max_concurrent_requests": config.max_concurrent_requests,
                "request_pool_size": config.request_pool_size
            }
            
            with open(self.main_config_file, 'w', encoding='utf-8') as f:
                json.dump(main_config_dict, f, indent=2, ensure_ascii=False)
            
            self._config = config
            logger.info("配置保存成功")
            
        except Exception as e:
            logger.error(f"配置保存失败: {e}")
            raise
    
    def get_config(self) -> TradingAgentsConfig:
        """
        获取当前配置
        
        @returns {TradingAgentsConfig} 当前配置对象
        """
        if self._config is None:
            return self.load_config()
        return self._config
    
    def update_config(self, **kwargs):
        """
        更新配置参数
        
        @param {**kwargs} kwargs - 要更新的配置参数
        """
        if self._config is None:
            self.load_config()
        
        # 更新配置
        for key, value in kwargs.items():
            if hasattr(self._config, key):
                setattr(self._config, key, value)
                logger.info(f"配置参数已更新: {key} = {value}")
            else:
                logger.warning(f"未知的配置参数: {key}")
        
        # 重新验证配置
        try:
            self._config._validate_config()
            self.save_config(self._config)
        except Exception as e:
            logger.error(f"配置更新失败: {e}")
            raise
    
    def reset_to_defaults(self):
        """重置为默认配置"""
        self._config = TradingAgentsConfig()
        self.save_config(self._config)
        logger.info("配置已重置为默认值")
    
    def export_config_template(self, file_path: str):
        """
        导出配置模板
        
        @param {str} file_path - 模板文件路径
        """
        template = {
            "description": "TradingAgents MVP配置模板",
            "version": "1.0",
            "environment_variables": {
                "required": [
                    "OPENAI_API_KEY"
                ],
                "optional": [
                    "ALPHA_VANTAGE_API_KEY",
                    "REDDIT_CLIENT_ID",
                    "REDDIT_CLIENT_SECRET",
                    "TRADING_CACHE_DIR",
                    "TRADING_LOG_LEVEL",
                    "TRADING_DEBUG_MODE",
                    "TRADING_WEB_PORT",
                    "TRADING_RISK_TOLERANCE"
                ]
            },
            "sample_config": TradingAgentsConfig().to_dict() if hasattr(TradingAgentsConfig(), 'to_dict') else {}
        }
        
        with open(file_path, 'w', encoding='utf-8') as f:
            json.dump(template, f, indent=2, ensure_ascii=False)
        
        logger.info(f"配置模板已导出到: {file_path}")


# 全局配置管理器实例
config_manager = ConfigManager()


def get_config() -> TradingAgentsConfig:
    """
    获取全局配置实例
    
    @returns {TradingAgentsConfig} 全局配置对象
    """
    return config_manager.get_config()


def update_config(**kwargs):
    """
    更新全局配置
    
    @param {**kwargs} kwargs - 要更新的配置参数
    """
    config_manager.update_config(**kwargs)


if __name__ == "__main__":
    # 测试配置系统
    print("=== TradingAgents免费配置系统测试 ===")
    
    try:
        # 创建配置管理器
        manager = ConfigManager("test_config")
        
        # 加载配置
        config = manager.load_config()
        print(f"✅ 配置加载成功")
        print(f"   - OpenAI模型: {config.openai_model}")
        print(f"   - 风险容忍度: {config.risk_tolerance}")
        print(f"   - 缓存TTL: {config.data_sources.cache_ttl_hours}小时")
        
        # 检查生产环境就绪状态
        is_ready = config.is_production_ready()
        print(f"   - 生产环境就绪: {'是' if is_ready else '否'}")
        
        # 获取成本估算
        cost_estimate = config.get_cost_estimate()
        print(f"   - 预估月成本: ${cost_estimate['monthly_cost_usd']}")
        print(f"   - 相比FinnHub节省: {cost_estimate['cost_savings_vs_finnhub']['savings_percentage']:.1f}%")
        
        # 保存配置
        manager.save_config(config)
        print(f"✅ 配置保存成功")
        
        # 导出配置模板
        manager.export_config_template("config_template.json")
        print(f"✅ 配置模板导出成功")
        
        print("\n🎉 配置系统测试完成！")
        
    except Exception as e:
        print(f"❌ 配置系统测试失败: {e}")
        raise