# Modified by TradingAgents-Studio contributors (2026) — see CHANGELOG.md
# Original: github.com/TauricResearch/TradingAgents (Apache License 2.0)

import os
from typing import Optional

_TRADINGAGENTS_HOME = os.path.join(os.path.expanduser("~"), ".tradingagents")

# Single source of truth for env-var → config-key overrides. To expose
# a new config key for environment-based override, add a row here — no
# entry-point script changes required. Coercion is driven by the type
# of the existing default, so users can keep writing plain strings in
# their .env file.
_ENV_OVERRIDES = {
    "TRADINGAGENTS_LLM_PROVIDER":         "llm_provider",
    "TRADINGAGENTS_DEEP_THINK_LLM":       "deep_think_llm",
    "TRADINGAGENTS_QUICK_THINK_LLM":      "quick_think_llm",
    "TRADINGAGENTS_LLM_BACKEND_URL":      "backend_url",
    "TRADINGAGENTS_OUTPUT_LANGUAGE":      "output_language",
    "TRADINGAGENTS_MAX_DEBATE_ROUNDS":    "max_debate_rounds",
    "TRADINGAGENTS_MAX_RISK_ROUNDS":      "max_risk_discuss_rounds",
    "TRADINGAGENTS_CHECKPOINT_ENABLED":   "checkpoint_enabled",
    "TRADINGAGENTS_BENCHMARK_TICKER":     "benchmark_ticker",
    # MediaCrawler DB credentials (sensitive — never hard-code passwords here)
    "TRADINGAGENTS_MC_DB_PASSWORD":       None,   # consumed by _get_mediacrawler_db_config
    "TRADINGAGENTS_MC_DB_NAME":           None,   # consumed by _get_mediacrawler_db_config
}


def _coerce(value: str, reference):
    """Coerce env-var string to the type of the existing default value."""
    if isinstance(reference, bool):
        return value.strip().lower() in ("true", "1", "yes", "on")
    if isinstance(reference, int) and not isinstance(reference, bool):
        return int(value)
    if isinstance(reference, float):
        return float(value)
    return value


def _apply_env_overrides(config: dict) -> dict:
    """Apply TRADINGAGENTS_* env vars to the config dict in-place."""
    for env_var, key in _ENV_OVERRIDES.items():
        raw = os.environ.get(env_var)
        if raw is None or raw == "":
            continue
        config[key] = _coerce(raw, config.get(key))
    return config


# ---------------------------------------------------------------------------
# MediaCrawler DB config helpers
# Credentials must come from environment variables, never from defaults.
# Usage:
#   TRADINGAGENTS_MC_DB_PASSWORD=your_password
#   TRADINGAGENTS_MC_DB_NAME=mediacrawler
# ---------------------------------------------------------------------------
def _get_mediacrawler_db_config(overrides: Optional[dict] = None) -> dict:
    """Build db_config dict with env vars as defaults.

    Supported env vars:
        TRADINGAGENTS_MC_DB_PASSWORD  → password field
        TRADINGAGENTS_MC_DB_NAME      → database field
    """
    cfg = {
        "host": os.getenv("TRADINGAGENTS_MC_DB_HOST", "localhost"),
        "port": int(os.getenv("TRADINGAGENTS_MC_DB_PORT", "3306")),
        "user": os.getenv("TRADINGAGENTS_MC_DB_USER", "root"),
        "password": os.environ.get("TRADINGAGENTS_MC_DB_PASSWORD", ""),
        "database": os.getenv("TRADINGAGENTS_MC_DB_NAME", "mediacrawler"),
    }
    if overrides:
        cfg.update(overrides)
    return cfg


DEFAULT_CONFIG = _apply_env_overrides({
    "project_dir": os.path.abspath(os.path.join(os.path.dirname(__file__), ".")),
    "results_dir": os.getenv("TRADINGAGENTS_RESULTS_DIR", os.path.join(_TRADINGAGENTS_HOME, "logs")),
    "data_cache_dir": os.getenv("TRADINGAGENTS_CACHE_DIR", os.path.join(_TRADINGAGENTS_HOME, "cache")),
    "memory_log_path": os.getenv("TRADINGAGENTS_MEMORY_LOG_PATH", os.path.join(_TRADINGAGENTS_HOME, "memory", "trading_memory.md")),
    # Optional cap on the number of resolved memory log entries. When set,
    # the oldest resolved entries are pruned once this limit is exceeded.
    # Pending entries are never pruned. None disables rotation entirely.
    "memory_log_max_entries": None,
    # LLM settings
    "llm_provider": "openai",
    "deep_think_llm": "gpt-5.4",
    "quick_think_llm": "gpt-5.4-mini",
    # When None, each provider's client falls back to its own default endpoint
    # (api.openai.com for OpenAI, generativelanguage.googleapis.com for Gemini, ...).
    # The CLI overrides this per provider when the user picks one. Keeping a
    # provider-specific URL here would leak (e.g. OpenAI's /v1 was previously
    # being forwarded to Gemini, producing malformed request URLs).
    "backend_url": None,
    # Provider-specific thinking configuration
    "google_thinking_level": None,      # "high", "minimal", etc.
    "openai_reasoning_effort": None,    # "medium", "high", "low"
    "anthropic_effort": None,           # "high", "medium", "low"
    # Checkpoint/resume: when True, LangGraph saves state after each node
    # so a crashed run can resume from the last successful step.
    "checkpoint_enabled": False,
    # Output language for analyst reports and final decision
    # Internal agent debate stays in English for reasoning quality
    "output_language": "Chinese",
    # Debate and discussion settings
    "max_debate_rounds": 1,
    "max_risk_discuss_rounds": 1,
    "max_recur_limit": 100,
    "analyst_concurrency_limit": 1,
    # News / data fetching parameters
    # Increase for longer lookback strategies or to broaden macro coverage;
    # decrease to reduce token usage in agent prompts.
    "news_article_limit": 20,             # max articles per ticker (ticker-news)
    "global_news_article_limit": 10,      # max articles for global/macro news
    "global_news_lookback_days": 7,       # macro news lookback window
    # Search queries used by get_global_news for macro headlines. Extend or
    # replace to broaden geographic / sector coverage.
    "global_news_queries": [
        "Federal Reserve interest rates inflation",
        "S&P 500 earnings GDP economic outlook",
        "geopolitical risk trade war sanctions",
        "ECB Bank of England BOJ central bank policy",
        "oil commodities supply chain energy",
    ],
    # Data vendor configuration
    # Category-level configuration (default for all tools in category)
    "data_vendors": {
        "core_stock_apis": "yfinance",       # Options: alpha_vantage, yfinance
        "technical_indicators": "yfinance",  # Options: alpha_vantage, yfinance
        "fundamental_data": "yfinance",      # Options: alpha_vantage, yfinance
        "news_data": "yfinance",             # Options: alpha_vantage, yfinance
    },
    # Tool-level configuration (takes precedence over category-level)
    "tool_vendors": {
        # Example: "get_stock_data": "alpha_vantage",  # Override category default
    },
    # A-share auto-routing chain. When a method is called with an A-share
    # ticker (6-digit code, .SS/.SH/.SZ suffix, or sh/sz prefix), these
    # vendors are tried first — regardless of ``data_vendors`` above —
    # falling back to the rest of the configured chain on failure.
    # AKShare is the recommended default (free, no key); Tushare is an
    # optional paid backup (requires TUSHARE_TOKEN env var).
    "cn_data_vendors": ["akshare", "tushare"],
    # A-share Chinese sentiment configuration
    # MediaCrawler DB credentials: set via environment variables
    #   TRADINGAGENTS_MC_DB_PASSWORD, TRADINGAGENTS_MC_DB_NAME,
    #   TRADINGAGENTS_MC_DB_HOST, TRADINGAGENTS_MC_DB_PORT, TRADINGAGENTS_MC_DB_USER
    "cn_sentiment_config": {
        "enabled": True,
        "eastmoney_guba": {
            "enabled": True,
            "limit": 50,
            "include_comments": True,
        },
        "mediacrawler": {
            "enabled": True,
            "platforms": ["weibo", "xhs"],
            "hours_back": 168,
            "limit": 50,
            "db_config": _get_mediacrawler_db_config(),
        },
    },
    # Benchmark for alpha calculation in the reflection layer.
    # ``benchmark_ticker`` (when set) overrides the suffix map for all
    # tickers; leave it None to use ``benchmark_map`` for auto-detection
    # based on the ticker's exchange suffix. SPY remains the US default
    # so the reflection label keeps reading "Alpha vs SPY" for US tickers
    # while non-US tickers get their regional index automatically.
    "benchmark_ticker": None,
    "benchmark_map": {
        ".NS":  "^NSEI",    # NSE India (Nifty 50)
        ".BO":  "^BSESN",   # BSE India (Sensex)
        ".T":   "^N225",    # Tokyo (Nikkei 225)
        ".HK":  "^HSI",     # Hong Kong (Hang Seng)
        ".L":   "^FTSE",    # London (FTSE 100)
        ".TO":  "^GSPTSE",  # Toronto (TSX Composite)
        ".AX":  "^AXJO",    # Australia (ASX 200)
        "":     "SPY",      # default for US-listed tickers (no suffix)
    },
})
