import os
import re
import secrets as _secrets
import logging
from pathlib import Path
from pydantic_settings import BaseSettings
from typing import Optional
from . import paths

logger = logging.getLogger(__name__)

# .env 文件路径
# 开发模式：bff-api/.env
# EXE 模式：数据目录/.env（双分区时在数据分区，单分区时在 EXE 目录）
_ENV_FILE = Path(paths.get_data_dir()) / ".env"

# 硬编码的不安全默认值
_INSECURE_JWT_DEFAULT = "standalone_secret_key_2026"


def _ensure_jwt_secret():
    """
    安全加固：确保 JWT_SECRET 不是硬编码的不安全默认值。
    首次使用时自动生成 256 位随机密钥并写入 .env 文件。
    """
    env_path = _ENV_FILE
    secret_key = "JWT_SECRET"

    # 检查 .env 中是否已有非默认值（空值也需要重新生成）
    if env_path.exists():
        content = env_path.read_text(encoding="utf-8")
        for line in content.splitlines():
            stripped = line.strip()
            if stripped.startswith(f"{secret_key}="):
                value = stripped[len(f"{secret_key}="):]
                if value and value != _INSECURE_JWT_DEFAULT:
                    return  # 已有有效的自定义密钥，无需处理

    # 生成随机密钥
    new_secret = _secrets.token_hex(32)
    logger.info("JWT_SECRET 使用默认不安全值，已自动生成随机密钥")

    # 追加或更新 .env
    if env_path.exists():
        content = env_path.read_text(encoding="utf-8")
        if secret_key in content:
            content = re.sub(
                rf"^{secret_key}=.*$",
                f"{secret_key}={new_secret}",
                content,
                flags=re.MULTILINE,
            )
        else:
            content = content.rstrip() + f"\n{secret_key}={new_secret}\n"
    else:
        content = f"{secret_key}={new_secret}\n"

    env_path.write_text(content, encoding="utf-8")
    # 更新运行时环境变量，Settings 实例化时会读取
    os.environ[secret_key] = new_secret


class Settings(BaseSettings):
    # 数据库引擎: "mysql" 或 "sqlite"
    DB_ENGINE: str = "sqlite"  # 单机版默认使用 SQLite
    DB_HOST: str = "localhost"
    DB_PORT: int = 3306
    DB_USER: str = "root"
    DB_PASSWORD: str = ""
    DB_NAME: str = "zfzs_standalone"
    # SQLite 路径（DB_ENGINE=sqlite 时使用）
    DB_PATH: str = "./data/zfzs_standalone.db"
    # 三库路径（相对于 EXE 目录）
    SYSTEM_DB_PATH: str = "./data/system.db"
    USER_DB_PATH: str = "./data/user.db"
    RUNTIME_DB_PATH: str = "./data/runtime.db"

    @property
    def DATABASE_URL(self) -> str:
        if self.DB_ENGINE == "sqlite":
            return f"sqlite+aiosqlite:///{self.DB_PATH}"
        return (
            f"mysql+aiomysql://{self.DB_USER}:{self.DB_PASSWORD}"
            f"@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}?charset=utf8mb4"
        )

    JWT_SECRET: str = "standalone_secret_key_2026"
    JWT_ALGORITHM: str = "HS256"
    JWT_EXPIRE_HOURS: int = 72  # 3天内免登录

    BFF_API_PORT: int = 4010
    CORS_ORIGINS: str = (
        "http://localhost:3808,http://localhost:3000,http://localhost:3090"
    )
    CORS_EXTRA_ORIGINS: str = ""

    EXPORT_DIR: str = "./exports"

    # LLM — 默认使用云端 API
    LLM_API_BASE: str = ""  # 留空则使用 SILICONFLOW_API_BASE
    LLM_API_KEY: str = ""
    LLM_MODEL: str = ""  # 留空则 fallback 到 AI_SCENE_CHAT_MODEL

    SILICONFLOW_API_KEY: str = ""
    SILICONFLOW_API_BASE: str = "https://api.siliconflow.cn/v1"
    RERANK_ENABLED: str = "true"
    RERANK_MODEL: str = "qllama/bge-reranker-v2-m3"

    COMPRESSION_ENABLED: bool = True
    COMPRESSION_MODEL: str = "qwen2.5:7b-instruct"
    COMPRESSION_TRIGGER_ROUNDS: int = 10
    COMPRESSION_TRIGGER_TOKENS: int = 8000
    COMPRESSION_TRIGGER_WINDOW_RATIO: float = 0.6

    # DeerFlow
    DEERFLOW_GATEWAY_URL: str = "http://localhost:2024"
    DEERFLOW_LANGGRAPH_URL: str = "http://localhost:2024"

    EMBEDDING_API_BASE: str = "https://api.siliconflow.cn/v1"
    EMBEDDING_API_KEY: str = ""
    EMBEDDING_MODEL: str = "bge-m3"

    UPLOAD_DIR: str = "./uploads"
    TEMPLATE_BASE_DIR: str = "./templates"

    # 路径配置（跨平台兼容，支持 Windows）
    MEMORIES_DIR: str = "./data/memories"       # 用户记忆文件目录
    ZHISHIKU_PATH: str = "./data/zhishiku"         # 知识库 Markdown 文件目录
    NEXT_PUBLIC_API_URL: str = "http://localhost:4010"

    # 僵尸Run清理配置
    RUN_TIMEOUT_SECONDS: int = 300
    CLEANUP_INTERVAL_SECONDS: int = 60
    ZOMBIE_THRESHOLD_SECONDS: int = 180

    MOCK_LOGIN: bool = False

    # Qdrant 向量数据库
    QDRANT_HOST: str = "localhost"
    QDRANT_PORT: int = 6334
    QDRANT_COLLECTION_NAME: str = "market_regulation_laws"

    ENCRYPTION_KEY: str = ""

    # 意见反馈邮件配置
    SMTP_HOST: str = "smtp.qq.com"
    SMTP_PORT: int = 465
    SMTP_USER: str = ""
    SMTP_PASSWORD: str = ""
    SMTP_TO: str = ""

    # AI Scene 配置（运行时从 system_configs 动态读取，以下为 .env 默认值）
    AI_SCENE_CHAT_MODEL: str = "Qwen/Qwen2.5-7B-Instruct"
    AI_SCENE_VISION_MODEL: str = "Qwen/Qwen3.5-4B"  # SiliconFlow 云端多模态（联网可用）
    AI_SCENE_EMBEDDING_MODEL: str = "bge-m3"
    AI_SCENE_RERANK_MODEL: str = "qllama/bge-reranker-v2-m3"

    # 视觉模型专用配置（SiliconFlow 云端）
    VISION_PROVIDER: str = "siliconflow"
    VISION_API_BASE: str = "https://api.siliconflow.cn/v1"
    VISION_API_KEY: str = ""
    VISION_MODEL: str = "Qwen/Qwen3.5-4B"

    model_config = {
        "env_file": str(_ENV_FILE),
        "env_file_encoding": "utf-8",
        "case_sensitive": True,
        "extra": "ignore",
    }


# 启动前确保 JWT_SECRET 安全
_ensure_jwt_secret()

settings = Settings()


async def get_upload_dir(db=None) -> str:
    """获取文书上传目录（优先读取用户配置，其次默认值）"""
    upload_dir = None
    # 优先从 config_paths 读取
    try:
        from ..api.admin.config_paths import get_document_save_path
        return await get_document_save_path()
    except Exception:
        pass
    # 回退：从 system_configs 读取旧配置
    if db:
        try:
            conn, cur = db
            await cur.execute(
                "SELECT config_value FROM system_configs WHERE config_key = 'upload_dir'"
            )
            row = await cur.fetchone()
            if row:
                val = dict(row).get("config_value", "").strip()
                if val:
                    upload_dir = val
        except Exception:
            pass
    if not upload_dir:
        upload_dir = settings.UPLOAD_DIR
    if not os.path.isabs(upload_dir):
        upload_dir = os.path.join(paths.get_exe_dir(), upload_dir)
    return upload_dir


# AI Scene 配置的合法 key 列表
AI_SCENE_KEYS = {"chat_model", "vision_model", "embedding_model", "rerank_model"}


def _encrypt_value(plain: str) -> str:
    """单机版直接明文存储，无需加密/编码"""
    return plain or ""


def _decrypt_value(encrypted: str) -> str:
    """解码：兼容旧 Base64/Fernet 格式，新数据为明文直通"""
    if not encrypted:
        return ""
    # 新格式：明文（不以 b64:/gAAAA 开头）
    if not encrypted.startswith("b64:") and not encrypted.startswith("gAAAA"):
        return encrypted
    # 旧格式：Base64 编码
    if encrypted.startswith("b64:"):
        import base64
        try:
            return base64.b64decode(encrypted[4:]).decode("utf-8")
        except Exception:
            return encrypted
    # 旧格式：Fernet 密文（gAAAA 开头），尝试解密
    if encrypted.startswith("gAAAA"):
        try:
            from cryptography.fernet import Fernet
            key = _legacy_get_encryption_key()
            f = Fernet(key.encode() if isinstance(key, str) else key)
            return f.decrypt(encrypted.encode()).decode("utf-8")
        except Exception:
            pass
    return encrypted


def _legacy_get_encryption_key() -> str:
    """获取旧版 Fernet 密钥（仅用于解密旧数据）"""
    key_file = os.path.join(paths.get_exe_dir(), "data", ".encryption_key")
    if os.path.exists(key_file):
        try:
            with open(key_file, "r", encoding="utf-8") as f:
                return f.read().strip()
        except Exception:
            pass
    return ""


async def get_ai_scene_config(scene_key: str, db=None) -> dict:
    """从 system_configs 读取 AI 场景配置，fallback 到 .env 默认值"""
    if scene_key not in AI_SCENE_KEYS:
        return {}

    # 尝试从数据库读取
    row = None
    if db:
        try:
            conn, cur = db
            await cur.execute(
                "SELECT config_value FROM system_configs WHERE config_key = %s",
                (f"ai_scene_{scene_key}",)
            )
            row = await cur.fetchone()
        except Exception:
            pass
    else:
        # 没有传入 db 连接时，自己创建一个来读取配置
        try:
            from .database import get_db, get_system_db, _has_split_databases
            _db_gen = get_system_db() if _has_split_databases() else get_db()
            async for conn, cur in _db_gen:
                await cur.execute(
                    "SELECT config_value FROM system_configs WHERE config_key = %s",
                    (f"ai_scene_{scene_key}",)
                )
                row = await cur.fetchone()
                break
        except Exception:
            pass

    if row:
        import json
        val = row["config_value"] if isinstance(row, dict) else (row[0] if row else None)
        if val:
            config = json.loads(val) if isinstance(val, str) else val
            # 解密 API Key
            if config.get("api_key"):
                try:
                    config["api_key_decrypted"] = _decrypt_value(config["api_key"])
                except Exception:
                    config["api_key_decrypted"] = config["api_key"]
            return config

    # Fallback 到 .env 默认值
    # vision_model 使用 SiliconFlow 云端多模态（Qwen3.5-4B）
    if scene_key == "vision_model":
        return {
            "provider": settings.VISION_PROVIDER,
            "api_base": settings.VISION_API_BASE,
            "api_key": settings.VISION_API_KEY,
            "api_key_decrypted": settings.VISION_API_KEY,
            "model": settings.VISION_MODEL,
            "enabled": True,
        }

    model_map = {
        "chat_model": settings.AI_SCENE_CHAT_MODEL,
        "embedding_model": settings.AI_SCENE_EMBEDDING_MODEL,
        "rerank_model": settings.AI_SCENE_RERANK_MODEL,
    }
    return {
        "provider": "siliconflow",
        "api_base": settings.SILICONFLOW_API_BASE,
        "api_key": "",
        "api_key_decrypted": "",
        "model": model_map.get(scene_key, ""),
        "enabled": True,
    }
