from __future__ import annotations

from functools import lru_cache

from pydantic_settings import BaseSettings, SettingsConfigDict


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

    mode: str = "SIM"
    tz: str = "Asia/Shanghai"
    env_name: str = "test"  # test / prod
    exchange: str = "okx"  # okx | binance（币安占位）

    api_host: str = "0.0.0.0"
    api_port: int = 5155

    # Web 登录（仅本机 .env，勿提交真密码）
    auth_username: str = "admin"
    auth_password: str = "admin123"
    auth_secret: str = "change-me-eth-hedge-sim-secret"
    auth_token_ttl_sec: int = 60 * 60 * 24 * 7
    auth_token_version: int = 1  # 改密时递增，作废旧 token
    disable_api_docs: bool = True  # 生产默认关闭 /docs
    login_max_attempts: int = 8
    login_window_sec: int = 300

    okx_api_key: str = ""
    okx_api_secret: str = ""
    okx_api_passphrase: str = ""
    okx_rest_base: str = "https://www.okx.com"
    okx_ws_public: str = "wss://ws.okx.com:8443/ws/v5/public"
    okx_http_proxy: str = ""

    # 币安私有交易密钥（LIVE 真下单：fapi 永续 + eapi 期权）
    binance_api_key: str = ""
    binance_api_secret: str = ""

    # 币安公共行情（SIM 只读）
    binance_fapi_base: str = "https://fapi.binance.com"
    binance_eapi_base: str = "https://eapi.binance.com"
    binance_futures_ws: str = "wss://fstream.binance.com/stream"
    # 欧式期权公共流（2025-12 起）：/public/stream；旧 nbstream/eoptions 已 404
    binance_options_ws: str = "wss://fstream.binance.com/public/stream"
    binance_http_proxy: str = ""

    perp_inst_id: str = "ETH-USDT-SWAP"
    option_inst_family: str = "ETH-USD_UM"
    index_inst_id: str = "ETH-USD"
    option_ct_mult_default: float = 0.01

    fee_rate: float = 0.0005
    initial_equity: float = 10_000.0  # SIM 模拟初始资金（USDT），设置页可改
    max_rounds: int = 3  # 已不再强管控，仅兼容旧字段
    open_hhmm: str = "16:00"  # 已废弃开仓窗
    stop_open_hhmm: str = "08:00"  # 已废弃开仓窗
    exit_move_points: float = 30.0  # 旧字段，已废弃
    exit_move_pct: float = 2.0  # 旧字段，已废弃（改用净盈利出场）
    exit_mode: str = "fixed_usdt"  # fixed_usdt | premium_multiple
    net_profit_target: float = 15.0  # fixed_usdt：净盈利 ≥ 该值（USDT）
    premium_exit_multiple: float = 1.0  # premium_multiple：净盈利 ≥ 权利金×倍数
    rest_seconds: int = 300
    live_order_interval_sec: float = 1.0  # LIVE 私有下单/查单最小间隔（秒）
    skip_weekends: bool = True  # 上海时区周六日禁止新开仓（已有仓仍可平）
    # 每个到期只开一次（跨日）：历史上已开过某 expiry_ymd 后不可再开同到期，只能盯下一档
    one_expiry_per_day: bool = True
    leverage: float = 3.0  # 永续杠杆
    # 永续保证金模式：cross=全仓（默认）| isolated=逐仓；期权仍固定 cash（OKX 逐仓/现金）
    perp_margin_mode: str = "cross"
    min_option_hours: float = 12.0  # 期权最小剩余小时
    min_option_leverage: float = 100.0  # 现价/卖一权利金 下限
    # 以损定仓权利金口径：actual=盘口卖一；selection=指数/选约杠杆（控节奏，默认）
    risk_leverage_basis: str = "selection"
    # 倍投：默认关；仅以损定仓+亏损幅度%且基础幅度≤3% 可开
    martingale_enabled: bool = False
    martingale_start_after_loss_days: int = 2  # 连续亏损 N 天后开始翻倍
    martingale_max_doubles: int = 3  # 最多翻倍次数（如 2→4→8→16 为 3 次）
    # 对冲模式：perp_option=永期（默认）| option_option=期期
    hedge_mode: str = "perp_option"
    oo_amplitude_pct: float = 1.5  # 振幅最大 %（过滤开启时：超过则不开）
    oo_amplitude_hours: float = 12.0  # 振幅回看小时（选约高低点；过滤关也用）
    oo_amplitude_filter_enabled: bool = False  # 振幅过滤开关（默认关）
    oo_min_option_hours: float = 24.0  # 期期：最短剩余到期小时
    oo_min_leverage: float = 200.0  # 期期：单腿最低杠杆
    oo_reward_ratio: float = 2.0  # 盈亏比：出场目标 = 预算 × 比
    oo_budget_cushion: float = 0.92  # 定仓预留余地（用于权利金的预算比例）
    oo_strike_max_dev_pct: float = 1.0  # 虚值行权价相对振幅高低点最大偏离 %
    atm_open_offset_enabled: bool = False  # 开仓 ATM 偏差限制开关（默认关）
    max_atm_open_offset: float = 3.0  # 开启后：|ATM行权价−标的| 上限（点）
    # 固定方向：关=现有 ATM/比价规则；开=指定永续多/空，期权 Put/Call 且须实值或平值
    fixed_direction_enabled: bool = False
    fixed_perp_side: str = "long"  # long|short；long→买Put，short→买Call
    # 半自动：系统设置开关；首页授权后盯开盯平，平完停
    semi_auto_enabled: bool = False
    semi_armed: bool = False  # 是否已授权本单（平完清零）
    semi_view_side: str = "long"  # long=买Call+永续空；short=买Put+永续多
    semi_option_move_points: float = 50.0  # 顺方向：标的波动点数
    semi_perp_exit_unit: float = 5.0  # 净利出场基数（×k）
    semi_min_option_hours: float = 30.0
    semi_min_option_leverage: float = 200.0  # 虚值默认 200（虚值门仍强制≥180）
    semi_moneyness: str = "otm"  # itm|atm|otm
    semi_otm_max_offset: float = 25.0  # 虚值 |K−S| 上限（可配）
    semi_perp_unit: float = 1.0  # 半自动永续:期权 配比分子
    semi_option_unit: float = 4.0  # 配比分母，默认 1:4
    close_bid_mark_max_pct: float = 30.0  # 平仓：买一相对标记最大偏差%
    # 残留期权中途平：当前买一权利金 ≥ 初始权利金 × 该% 才尝试卖出
    residual_min_premium_pct: float = 20.0
    residual_close_check_sec: int = 300  # 残留巡检间隔（秒）
    perp_qty_eth: float = 1.0
    option_qty_eth: float = 2.0
    db_path: str = ""  # empty -> backend/data/hedge.db

    # 企业微信群机器人
    wecom_enabled: bool = False
    wecom_webhook_url: str = ""
    wecom_machine_name: str = ""

    @property
    def is_sim(self) -> bool:
        return self.mode.strip().upper() != "LIVE"


# 切换交易所时的合约默认
EXCHANGE_MARKET_DEFAULTS: dict[str, dict[str, str | float]] = {
    "okx": {
        "perp_inst_id": "ETH-USDT-SWAP",
        "option_inst_family": "ETH-USD_UM",
        "index_inst_id": "ETH-USD",
        "option_ct_mult_default": 0.01,
    },
    "binance": {
        "perp_inst_id": "ETHUSDT",
        "option_inst_family": "ETHUSDT",
        "index_inst_id": "ETHUSDT",
        "option_ct_mult_default": 1.0,
    },
}


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