from pydantic_settings import BaseSettings
from functools import lru_cache


class Settings(BaseSettings):
    # Angel One credentials (legacy ENV fallback — optional if broker is configured in the DB)
    angel_api_key: str = ""
    angel_client_id: str = ""
    angel_password: str = ""
    angel_totp_secret: str = ""

    # AI
    anthropic_api_key: str = ""

    # App
    app_env: str = "development"
    log_level: str = "INFO"

    # ── Broker HTTP forensics ────────────────────────────────────────
    # Emits one `BROKER_HTTP` line per outbound Angel One HTTP round-trip:
    # status, headers, `Retry-After`, timing and a safe body classification,
    # read at `requests.adapters.HTTPAdapter.send` before the SmartAPI SDK
    # discards them. Observation only — it changes no limit, no cadence, no
    # retry and no header (`brokers/http_forensics.py` says why at length).
    #
    # Default **on**: the 2026-08-27 rate-limit investigation needs this
    # evidence and cannot be answered without it. The switch exists so an
    # operator can silence the log volume of a long backfill without editing
    # code, not because the instrumentation is risky to leave running.
    broker_http_forensics: bool = True

    # ── Broker identity/transport experiment ─────────────────────────
    # `off` (default) | `real_ip` | `pooled` | `schedule`.
    #
    # A measurement with a reversible flag, **not** a fix. It separates the
    # 2026-08-27 rate-limit hypotheses by changing what a request *looks
    # like* — one variable per arm — while the cadence, the coordinator and
    # every trading decision stay exactly as they are:
    #
    #   off       byte-identical to stock. The control arm.
    #   real_ip   `X-ClientPublicIP` carries this machine's true public IP
    #             instead of the `106.193.147.98` that smartapi-python 1.3.4
    #             hardcodes for every installation of it. Headers only.
    #   pooled    candle requests reuse one process-wide connection pool.
    #             Headers unchanged. Transport only.
    #   schedule  rotates off → real_ip → pooled in fixed blocks, so one
    #             session yields interleaved samples of all three.
    #
    # Default **off**: no arm may be enabled by accident, and the permanent
    # fix is a human decision made from the readout
    # (`scripts/analyze_identity_experiment.py`), not from this default.
    # `brokers/identity_experiment.py` is the account.
    broker_identity_experiment: str = "off"

    #: Rotation block length for `schedule` mode, in minutes.
    broker_identity_experiment_block_minutes: int = 20

    # MySQL
    db_host: str = "127.0.0.1"
    db_port: int = 8889
    db_user: str = "root"
    db_password: str = "root"
    db_name: str = "market_scanner"

    def database_url(self) -> str:
        return (
            f"mysql+pymysql://{self.db_user}:{self.db_password}"
            f"@{self.db_host}:{self.db_port}/{self.db_name}"
            f"?charset=utf8mb4"
        )

    # Risk limits
    max_daily_loss: float = 5000.0
    max_open_positions: int = 3
    max_lot_size: int = 1

    # Auth & Security
    jwt_secret: str = "change-this-secret-in-production-min-32-chars!!"
    jwt_algorithm: str = "HS256"
    jwt_access_token_expire_minutes: int = 1440
    jwt_refresh_token_expire_days: int = 7
    fernet_key: str = ""

    # ── Super Admin (isolated auth) ──────────────────────────────────
    admin_jwt_secret: str = "change-admin-secret-in-production-min-32-chars!!"
    admin_jwt_algorithm: str = "HS256"
    admin_access_token_expire_minutes: int = 60
    admin_refresh_token_expire_hours: int = 8
    admin_bootstrap_email: str = ""
    admin_bootstrap_password: str = ""
    admin_bootstrap_name: str = "Super Admin"

    # App
    app_url: str = "http://localhost:5173"

    # Email (optional)
    smtp_host: str = ""
    smtp_port: int = 587
    smtp_user: str = ""
    smtp_password: str = ""
    smtp_from: str = "noreply@algotrader.com"

    class Config:
        env_file = ".env"
        extra = "ignore"


@lru_cache()
def get_settings() -> Settings:
    return Settings()


# NSE market session times (IST) — **UNUSED, and not to be wired up.**
#
# Nothing imports these. They are a pre-2026 copy that drifted: the close here
# is the legacy 15:30 the SEBI/NSE change of 2026-08-03 replaced, and the
# square-off is 15:20 where the platform's actual policy is 15:14. Reading them
# would silently reinstate the old market structure.
#
# The owners are `data/market_sessions.py` (exchange timings, segment- and
# date-aware) and `data/market_hours.py` (this platform's entry and square-off
# policy). Kept only so a stale external reference fails loudly at review
# rather than quietly at runtime; delete on the next pass that can prove no
# deployment reads them.
MARKET_OPEN_H, MARKET_OPEN_M = 9, 15
MARKET_CLOSE_H, MARKET_CLOSE_M = 15, 30
NO_NEW_ENTRY_H, NO_NEW_ENTRY_M = 15, 15
AUTO_SQUAREOFF_H, AUTO_SQUAREOFF_M = 15, 20

# Supported instruments
INDICES = ["NIFTY", "BANKNIFTY", "FINNIFTY"]

# Strategy thresholds
OI_CHANGE_THRESHOLD = 0.05       # 5% OI change to trigger signal
VOLUME_SPIKE_MULTIPLIER = 2.0    # volume must be 2x 20-period avg
IV_SELL_THRESHOLD = 80           # IV percentile above which to consider selling
IV_BUY_THRESHOLD = 20            # IV percentile below which to consider buying
DELTA_MIN = 0.30                 # minimum delta for strike selection
DELTA_MAX = 0.55                 # maximum delta for strike selection
THETA_EXIT_THRESHOLD = -500.0    # auto-exit if daily theta exceeds this
