from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore")

    # Database
    database_url: str = "postgresql+asyncpg://stock_backend:changeme@localhost:5432/stock_data_backend"

    # Auth
    jwt_secret: str = "change-this-to-a-random-secret"
    jwt_expiry_minutes: int = 60

    # OHLCV Provider — Alpaca Markets
    alpaca_api_key: str = ""
    alpaca_api_secret: str = ""

    # Sentiment Provider — Gemini with Search Grounding (legacy)
    gemini_api_key: str = ""
    gemini_model: str = "gemini-2.0-flash"

    # Sentiment Provider — OpenAI
    openai_api_key: str = ""
    openai_model: str = "gpt-4o-mini"
    openai_sentiment_batch_size: int = 5

    # Sentiment Provider — DeepSeek / xAI (OpenAI-compatible; optional env fallback)
    deepseek_api_key: str = ""
    xai_api_key: str = ""

    # Fundamentals Provider — Financial Modeling Prep
    fmp_api_key: str = ""

    # Fundamentals Provider — Finnhub (optional fallback)
    finnhub_api_key: str = ""

    # Fundamentals Provider — Alpha Vantage (optional fallback)
    alpha_vantage_api_key: str = ""

    # Regime Monitor — FRED (VIX level + HY credit spreads). Optional: without it
    # the volatility (P5) and credit-spread (F2) signals are reported as n/a.
    fred_api_key: str = ""

    # Alerts — Telegram (optional env fallback; can also be set in Admin)
    telegram_bot_token: str = ""
    telegram_chat_id: str = ""

    # Scheduled Jobs
    data_collector_frequency: str = "daily"
    sentiment_poll_interval_minutes: int = 30
    # Sentiment search-budget controls (Gemini grounding free tier = 5000/month).
    # Scope (see _get_sentiment_priority_tickers): everything that matters is always
    # refreshed in full — open paper trades + the curated watchlist + top-pick
    # feeders (residual-momentum leaders with a tradeable long setup) — plus a top-N composite
    # discovery net. No per-run cap: the set is naturally bounded (watchlist <= 20,
    # composite <= top_composite), so a full refresh stays well inside the free tier.
    # Skip anything refreshed within fresh_hours (5 days: sentiment shifts slowly and
    # the score window is 7 days).
    sentiment_fresh_hours: int = 120
    sentiment_top_composite: int = 30
    fundamental_fetch_frequency: str = "weekly"  # quarterly-ish data; weekly conserves API quota
    rr_scan_frequency: str = "daily"
    alerts_frequency: str = "hourly"
    fundamental_rate_limit_retries: int = 3
    fundamental_rate_limit_backoff_seconds: int = 15
    # Pause between tickers in the bulk fundamentals job. Free tiers throttle
    # hard (Finnhub ~60 calls/min, ~3 calls/ticker → ~3s/ticker); without
    # spacing the job bursts straight into 429s. 0 disables.
    fundamental_request_spacing_seconds: float = 3.0

    # Scoring Defaults
    default_watchlist_auto_size: int = 10
    default_rr_threshold: float = 1.5

    # Outcome evaluation: trading days before an undecided setup expires
    outcome_evaluation_max_bars: int = 30

    # OHLCV history depth to fetch. New tickers backfill this far; the manual
    # "data_backfill" job re-fetches the full window for everyone. ~5 years so
    # long-lookback factors (12-month momentum, 52-week high) and multi-regime
    # backtests become computable. ~252 trading days/year.
    ohlcv_history_days: int = 1825

    # Backtest parallelism: replay tickers across this many worker processes on
    # POSIX (forkserver), capped to cpu_count-1 so a core stays free for the web
    # server. 1 disables it (sequential). No effect on Windows / spawn-only
    # platforms — those fall back to a single worker thread.
    backtest_workers: int = 4

    # Database Pool
    db_pool_size: int = 5
    db_pool_timeout: int = 30

    # Logging
    log_level: str = "INFO"


settings = Settings()
