"""
后端配置加载

优先级（低 → 高）：根 .env → 应用 .env.local → 环境变量
敏感值（DSN、Token）禁止提交，仅放在 .env / .env.local 中。
"""

import os
from pathlib import Path

from dotenv import load_dotenv

# CHANGE 2026-03-07 | 项目根目录定位：防止 junction/symlink 穿透到 D 盘
# 背景：C:\Project\NeoZQYY 是 junction → D:\NeoZQYY\...\repo，
# Path(__file__).resolve() 和 absolute() 都可能解析到 D 盘，
# 导致加载 D 盘的 .env（路径全指向 D 盘），ETL 命令因此携带错误路径。
# 策略：环境变量 > 已知固定路径 > __file__ 推算（最后手段）
import logging as _logging
_cfg_logger = _logging.getLogger("app.config")


def _find_project_root() -> Path:
    """定位项目根目录，返回包含 .env 的路径。

    优先级：
    1. 环境变量 NEOZQYY_ROOT（最可靠，显式指定）
    2. __file__ 向上推算，但用 junction 安全的方式
    """

    # 1. 环境变量显式指定（部署时设置，最可靠）
    env_root = os.environ.get("NEOZQYY_ROOT")
    if env_root:
        p = Path(env_root)
        if (p / ".env").exists():
            _cfg_logger.info("[ROOT] 策略1命中: NEOZQYY_ROOT=%s", p)
            return p
        _cfg_logger.warning("[ROOT] NEOZQYY_ROOT=%s 但 .env 不存在，跳过", env_root)

    # 2. 从 __file__ 推算（apps/backend/app/config.py → 上 3 级）
    raw_file = Path(__file__)
    abs_file = raw_file.absolute()
    candidate = abs_file.parents[3]

    _cfg_logger.info(
        "[ROOT] 策略2: __file__=%s | absolute=%s | candidate=%s",
        raw_file, abs_file, candidate,
    )

    # CHANGE 2026-03-07 | 防护：如果推算路径包含 test/repo 或 prod/repo 等
    # 多环境子目录，说明发生了 junction/symlink 穿透到 D 盘部署结构，
    # 此时向上搜索找到真正的项目根（包含 .env 的最浅目录）
    candidate_str = str(candidate)
    if any(seg in candidate_str for seg in ("\\test\\", "\\prod\\", "/test/", "/prod/")):
        _cfg_logger.warning(
            "[ROOT] 检测到多环境子目录穿透: %s，启动向上搜索", candidate_str
        )
    elif (candidate / ".env").exists():
        _cfg_logger.info("[ROOT] 策略2命中: %s", candidate)
        return candidate

    # 3. 向上搜索：应对 junction 穿透导致层级偏移的情况
    cur = abs_file.parent
    for i in range(10):
        if (cur / ".env").exists():
            _cfg_logger.info("[ROOT] 策略3命中(第%d级): %s", i, cur)
            return cur
        parent = cur.parent
        if parent == cur:
            break
        cur = parent

    _cfg_logger.warning("[ROOT] 所有策略均未命中，回退到: %s", candidate)
    return candidate


_project_root = _find_project_root()
_cfg_logger.info("项目根目录: %s", _project_root)

# 根 .env（公共配置）
_root_env = _project_root / ".env"
_cfg_logger.info("加载根 .env: %s (存在: %s)", _root_env, _root_env.exists())
load_dotenv(_root_env, override=False)

# 应用级 .env.local（私有覆盖，优先级更高）
_local_env = _project_root / "apps" / "backend" / ".env.local"
load_dotenv(_local_env, override=True)


def get(key: str, default: str | None = None) -> str | None:
    """从环境变量读取配置值。"""
    return os.getenv(key, default)


def _require_env(key: str) -> str:
    """必需的环境变量，缺失时立即报错。"""
    raise RuntimeError(
        f"必需的环境变量 {key} 未设置。"
        f"请在 .env 中显式配置（当前 .env 路径: {_root_env}）"
    )


# ---- 数据库连接参数 ----
DB_HOST: str = get("DB_HOST", "localhost")
DB_PORT: str = get("DB_PORT", "5432")
DB_USER: str = get("DB_USER", "")
DB_PASSWORD: str = get("DB_PASSWORD", "")
# CHANGE 2026-02-15 | 默认指向测试库，生产环境通过 .env 覆盖
APP_DB_NAME: str = get("APP_DB_NAME", "test_zqyy_app")

# ---- JWT 认证 ----
JWT_SECRET_KEY: str = get("JWT_SECRET_KEY", "")  # 生产环境必须设置
JWT_ALGORITHM: str = get("JWT_ALGORITHM", "HS256")
JWT_ACCESS_TOKEN_EXPIRE_MINUTES: int = int(get("JWT_ACCESS_TOKEN_EXPIRE_MINUTES", "30"))
# CHANGE 2026-03-27 | 权限改造 W1：refresh_token 有效期 7天→30天，配合滑动窗口续期
JWT_REFRESH_TOKEN_EXPIRE_DAYS: int = int(get("JWT_REFRESH_TOKEN_EXPIRE_DAYS", "30"))

# ---- ETL 数据库连接参数（可独立配置，缺省时复用 zqyy_app 的连接参数） ----
ETL_DB_HOST: str = get("ETL_DB_HOST") or DB_HOST
ETL_DB_PORT: str = get("ETL_DB_PORT") or DB_PORT
ETL_DB_USER: str = get("ETL_DB_USER") or DB_USER
ETL_DB_PASSWORD: str = get("ETL_DB_PASSWORD") or DB_PASSWORD
# CHANGE 2026-02-15 | 默认指向测试库，生产环境通过 .env 覆盖
ETL_DB_NAME: str = get("ETL_DB_NAME", "test_etl_feiqiu")

# ---- CORS ----
# 逗号分隔的允许来源列表；缺省允许 Vite 开发服务器
CORS_ORIGINS: list[str] = [
    o.strip()
    for o in get("CORS_ORIGINS", "http://localhost:5173").split(",")
    if o.strip()
]

# ---- ETL 项目路径 ----
# CHANGE 2026-03-06 | 必须在 .env 显式设置，禁止依赖 __file__ 推算
# 原因：__file__ 推算依赖 uvicorn 启动位置，不同部署环境会指向错误代码副本
ETL_PROJECT_PATH: str = get("ETL_PROJECT_PATH") or _require_env("ETL_PROJECT_PATH")

# ETL 子进程 Python 可执行路径
# CHANGE 2026-03-06 | 必须在 .env 显式设置，避免 PATH 歧义
ETL_PYTHON_EXECUTABLE: str = get("ETL_PYTHON_EXECUTABLE") or _require_env("ETL_PYTHON_EXECUTABLE")

# ---- 运维面板 ----
# CHANGE 2026-03-06 | 必须在 .env 显式设置，消除 __file__ 推算风险
OPS_SERVER_BASE: str = get("OPS_SERVER_BASE") or _require_env("OPS_SERVER_BASE")

# CHANGE 2026-03-07 | 启动时验证关键路径：
# 1. 路径必须实际存在于文件系统
# 2. 路径不得包含多环境子目录（test/repo、prod/repo），这是 junction 穿透的标志
_cfg_logger.info("ETL_PROJECT_PATH = %s", ETL_PROJECT_PATH)
_cfg_logger.info("ETL_PYTHON_EXECUTABLE = %s", ETL_PYTHON_EXECUTABLE)
_cfg_logger.info("OPS_SERVER_BASE = %s", OPS_SERVER_BASE)

for _var_name, _var_val in [
    ("ETL_PROJECT_PATH", ETL_PROJECT_PATH),
    ("ETL_PYTHON_EXECUTABLE", ETL_PYTHON_EXECUTABLE),
    ("OPS_SERVER_BASE", OPS_SERVER_BASE),
]:
    # 检测 junction 穿透特征：路径中包含 \test\repo 或 \prod\repo
    _normalized = _var_val.replace("/", "\\")
    if "\\test\\repo" in _normalized or "\\prod\\repo" in _normalized:
        _cfg_logger.error(
            "路径穿透检测: %s=%s 包含多环境子目录，"
            "说明 .env 来自 junction 穿透后的 D 盘副本。"
            "当前 .env 路径: %s | NEOZQYY_ROOT: %s",
            _var_name, _var_val, _root_env,
            os.environ.get("NEOZQYY_ROOT", "<未设置>"),
        )
        raise RuntimeError(
            f"配置路径异常: {_var_name}={_var_val} 包含多环境子目录"
            f"（test/repo 或 prod/repo），疑似加载了错误的 .env。"
            f" 当前 .env: {_root_env}。"
            f" 请确保 NEOZQYY_ROOT 环境变量指向正确的项目根目录。"
        )

# ---- 微信小程序 ----
WX_APPID: str = get("WX_APPID", "")
WX_SECRET: str = get("WX_SECRET", "")
# 开发模式：WX_DEV_MODE=true 时启用 mock 登录端点，跳过微信 code2Session
WX_DEV_MODE: bool = get("WX_DEV_MODE", "false").lower() in ("true", "1", "yes")

# ---- 用户头像存储 ----
# chooseAvatar 上传后保存到此目录，文件名 {user_id}.jpg
AVATAR_EXPORT_PATH: str = get("AVATAR_EXPORT_PATH", "")

# ---- 营业日分割点 ----
BUSINESS_DAY_START_HOUR: int = int(get("BUSINESS_DAY_START_HOUR", "8"))

# ---- 通用 ----
TIMEZONE: str = get("TIMEZONE", "Asia/Shanghai")
LOG_LEVEL: str = get("LOG_LEVEL", "INFO")
