"""配置管理 — 三层合并：默认值 → ~/.claude-index1/config.yaml → 项目 .index1.yaml"""

from __future__ import annotations

import os
from dataclasses import dataclass, field, fields
from pathlib import Path

import yaml

# 全局配置目录（支持 INDEX1_HOME 环境变量覆盖）
INDEX1_HOME = Path(os.environ["INDEX1_HOME"]) if os.environ.get("INDEX1_HOME") else Path.home() / ".claude-index1"
GLOBAL_CONFIG_FILE = INDEX1_HOME / "config.yaml"
PROJECT_CONFIG_FILE = ".index1.yaml"

# 预定义模型档位
MODEL_PROFILES: dict[str, dict[str, str | int]] = {
    "lightweight": {
        "model": "all-minilm",
        "dim": 384,
        "desc": "轻量级，英文为主，~45MB 磁盘 / ~250MB 内存",
    },
    "standard": {
        "model": "nomic-embed-text",
        "dim": 768,
        "desc": "推荐默认，中英文通用，~270MB 磁盘 / ~500MB 内存",
    },
    "high_precision": {
        "model": "nomic-embed-text-v2-moe",
        "dim": 768,
        "desc": "高精度多语言，~1GB 磁盘 / ~1GB 内存",
    },
    "chinese": {
        "model": "bge-m3",
        "dim": 1024,
        "desc": "中文最优，支持 100+ 语言，~1.2GB 磁盘 / ~1.2GB 内存",
    },
    "multilingual": {
        "model": "paraphrase-multilingual",
        "dim": 768,
        "desc": "多语言通用（50+ 语言），中日韩英均衡，~1GB 磁盘 / ~1GB 内存",
    },
}


@dataclass
class Config:
    """index1 核心配置"""

    db_path: str = ""  # 空字符串表示使用默认路径
    embedding_model: str = "nomic-embed-text"
    embedding_dim: int = 768
    ollama_url: str = "http://localhost:11434"
    top_k: int = 10
    rrf_k: int = 60
    watch_paths: list[str] = field(default_factory=list)
    collection: str = "default"
    rerank_enabled: bool = False
    rerank_model: str = "bge-reranker-v2-m3"

    def __post_init__(self):
        if not self.db_path:
            self.db_path = str(INDEX1_HOME / "knowledge.db")

    @property
    def db_path_resolved(self) -> Path:
        return Path(self.db_path).expanduser()


def _load_yaml(path: Path) -> dict:
    """加载 YAML 文件，不存在或解析失败返回空 dict"""
    if not path.is_file():
        return {}
    try:
        with open(path, encoding="utf-8") as f:
            data = yaml.safe_load(f)
            return data if isinstance(data, dict) else {}
    except (OSError, yaml.YAMLError):
        return {}


def _merge_into_config(config: Config, overrides: dict) -> None:
    """将 dict 中的有效键合并到 Config 实例"""
    valid_fields = {f.name for f in fields(Config)}
    for key, value in overrides.items():
        if key in valid_fields and value is not None:
            setattr(config, key, value)


def load_config(project_path: Path | None = None) -> Config:
    """加载配置，三层合并：默认值 → 全局 → 项目级

    Args:
        project_path: 项目根目录，用于查找 .index1.yaml。
                      None 时使用当前工作目录。
    """
    # 确保配置目录存在
    try:
        INDEX1_HOME.mkdir(parents=True, exist_ok=True)
    except OSError:
        pass

    # Layer 1: 默认值
    config = Config()

    # Layer 2: 全局配置 ~/.claude-index1/config.yaml
    global_overrides = _load_yaml(GLOBAL_CONFIG_FILE)
    _merge_into_config(config, global_overrides)

    # Layer 3: 项目配置 .index1.yaml
    if project_path is None:
        project_path = Path.cwd()
    project_config = project_path / PROJECT_CONFIG_FILE
    project_overrides = _load_yaml(project_config)
    _merge_into_config(config, project_overrides)

    return config


def _get_memory_gb() -> float:
    """获取系统物理内存 (GB)，失败返回 16.0 作为默认值"""
    try:
        import os
        if hasattr(os, "sysconf"):
            # macOS + Linux
            page_size = os.sysconf("SC_PAGE_SIZE")
            phys_pages = os.sysconf("SC_PHYS_PAGES")
            if page_size > 0 and phys_pages > 0:
                return (page_size * phys_pages) / (1024 ** 3)
        else:
            # Windows
            import ctypes
            kernel32 = ctypes.windll.kernel32
            mem_kb = ctypes.c_ulonglong()
            ret = kernel32.GetPhysicallyInstalledSystemMemory(ctypes.byref(mem_kb))
            if ret and mem_kb.value > 0:
                return (mem_kb.value * 1024) / (1024 ** 3)
    except (AttributeError, ValueError, OSError):
        pass
    return 16.0


def auto_select_model() -> str:
    """根据系统内存自动选择 embedding 模型档位

    自动选择（按内存）:
    - < 12 GB: lightweight (all-minilm, 384 维)
    - 12-32 GB: standard (nomic-embed-text, 768 维)
    - >= 32 GB: high_precision (nomic-embed-text-v2-moe, 768 维)

    用户手动选择（按语言需求）:
    - chinese: bge-m3 (1024 维) — 中文最优，100+ 语言
    - multilingual: paraphrase-multilingual (768 维) — 50+ 语言通用

    Returns:
        MODEL_PROFILES 中的 key
    """
    mem_gb = _get_memory_gb()

    if mem_gb < 12:
        return "lightweight"
    elif mem_gb >= 32:
        return "high_precision"
    return "standard"


def save_config(config: Config, path: Path | None = None) -> None:
    """保存配置到 YAML 文件

    Args:
        path: 保存路径。None 时保存到全局配置文件。
    """
    if path is None:
        path = GLOBAL_CONFIG_FILE
    try:
        path.parent.mkdir(parents=True, exist_ok=True)
    except OSError:
        pass

    data = {}
    default = Config()
    for f in fields(Config):
        value = getattr(config, f.name)
        default_value = getattr(default, f.name)
        # 只保存非默认值
        if value != default_value:
            data[f.name] = value

    try:
        with open(path, "w", encoding="utf-8") as f:
            yaml.dump(data, f, default_flow_style=False, allow_unicode=True)
    except OSError:
        pass
