"""
Configuration loader for Elasticsearch MCP.
Supports:
1. profiles.json - Local configuration file
2. Environment variables - For sensitive data (hosts, username, password)
3. getpass() fallback - Interactive password input
"""
from __future__ import annotations

import getpass
import json
import os
import sys
from pathlib import Path

from .models import AppConfig, ConnectionProfile, EnvVars, SafetyConfig, SafetyPolicy


# 配置文件路径
CONFIG_DIR = Path.home() / ".config" / "elasticsearch-client-mcp"
CONFIG_FILE = CONFIG_DIR / "profiles.json"
SETUP_COMPLETE_FLAG = CONFIG_DIR / ".setup_complete"


def _ensure_config_dir() -> None:
    """确保配置目录存在"""
    CONFIG_DIR.mkdir(parents=True, exist_ok=True)


def _parse_bool(val: str | None) -> bool:
    if val is None:
        return False
    return str(val).strip().lower() in ("1", "true", "yes", "on")


def load_safety_config() -> SafetyConfig:
    """从 profiles.json 加载安全配置"""
    safety_config = SafetyConfig()

    if CONFIG_FILE.exists():
        try:
            with open(CONFIG_FILE, "r", encoding="utf-8") as f:
                data = json.load(f)

            # 加载基础配置
            safety_data = data.get("safety", {})

            safety_config.read_only = _parse_bool(safety_data.get("read_only"))
            safety_config.max_search_size = int(safety_data.get("max_search_size", 100))
            safety_config.request_timeout = int(safety_data.get("request_timeout", 30))
            safety_config.confirm_write = _parse_bool(safety_data.get("confirm_write", True))
            safety_config.confirm_delete = _parse_bool(safety_data.get("confirm_delete", True))
            safety_config.confirm_index = _parse_bool(safety_data.get("confirm_index", True))

            # 加载策略配置
            policy_data = safety_data.get("policy", {})
            safety_config.policy = SafetyPolicy(
                allow_search=_parse_bool(policy_data.get("allow_search", True)),
                allow_index=_parse_bool(policy_data.get("allow_index", True)),
                allow_update=_parse_bool(policy_data.get("allow_update", True)),
                allow_delete=_parse_bool(policy_data.get("allow_delete", True)),
                allow_bulk=_parse_bool(policy_data.get("allow_bulk", True)),
                allow_create_index=_parse_bool(policy_data.get("allow_create_index", False)),
                allow_delete_index=_parse_bool(policy_data.get("allow_delete_index", False)),
                allow_update_mapping=_parse_bool(policy_data.get("allow_update_mapping", False)),
                allow_cluster=_parse_bool(policy_data.get("allow_cluster", False)),
                danger_path_patterns=policy_data.get("danger_path_patterns", [
                    "/_cluster", "/_nodes", "/_snapshot", "/_close", "/_open"
                ]),
            )
        except (json.JSONDecodeError, IOError) as e:
            print(f"Warning: Failed to load safety config from {CONFIG_FILE}: {e}", file=sys.stderr)

    # Fallback 到环境变量
    safety_config.read_only = _parse_bool(os.environ.get("ES_READ_ONLY"))
    if os.environ.get("ES_MAX_SEARCH_SIZE"):
        safety_config.max_search_size = int(os.environ.get("ES_MAX_SEARCH_SIZE", 100))
    if os.environ.get("ES_REQUEST_TIMEOUT"):
        safety_config.request_timeout = int(os.environ.get("ES_REQUEST_TIMEOUT", 30))

    return safety_config


def save_safety_config(safety: SafetyConfig) -> None:
    """保存安全配置到 profiles.json"""
    _ensure_config_dir()

    data = {}
    if CONFIG_FILE.exists():
        try:
            with open(CONFIG_FILE, "r", encoding="utf-8") as f:
                data = json.load(f)
        except (json.JSONDecodeError, IOError):
            pass

    data["safety"] = {
        "read_only": safety.read_only,
        "max_search_size": safety.max_search_size,
        "request_timeout": safety.request_timeout,
        "confirm_write": safety.confirm_write,
        "confirm_delete": safety.confirm_delete,
        "confirm_index": safety.confirm_index,
        "policy": {
            "allow_search": safety.policy.allow_search,
            "allow_index": safety.policy.allow_index,
            "allow_update": safety.policy.allow_update,
            "allow_delete": safety.policy.allow_delete,
            "allow_bulk": safety.policy.allow_bulk,
            "allow_create_index": safety.policy.allow_create_index,
            "allow_delete_index": safety.policy.allow_delete_index,
            "allow_update_mapping": safety.policy.allow_update_mapping,
            "allow_cluster": safety.policy.allow_cluster,
            "danger_path_patterns": safety.policy.danger_path_patterns,
        },
    }

    with open(CONFIG_FILE, "w", encoding="utf-8") as f:
        json.dump(data, f, indent=2, ensure_ascii=False)


def _get_password_interactive(env_var_name: str | None) -> str:
    """交互式获取密码（fallback）。非交互环境下返回空字符串。"""
    if not sys.stdin.isatty():
        # 非交互环境（如 MCP server 启动时），返回空字符串
        return ""
    if env_var_name:
        return getpass.getpass(f"Enter password for {env_var_name}: ")
    return getpass.getpass("Enter ES password: ")


def load_config() -> AppConfig:
    """加载完整配置"""
    _ensure_config_dir()

    config = AppConfig()

    # 1. 先尝试从 profiles.json 加载
    if CONFIG_FILE.exists():
        try:
            with open(CONFIG_FILE, "r", encoding="utf-8") as f:
                data = json.load(f)

            profiles = data.get("profiles", [])
            for p in profiles:
                env_vars_data = p.get("env_vars", {})
                env_vars = EnvVars(
                    hosts=env_vars_data.get("hosts"),
                    username=env_vars_data.get("username"),
                    password=env_vars_data.get("password"),
                )
                profile = ConnectionProfile(
                    name=p.get("name", ""),
                    env=p.get("env", "test"),
                    description=p.get("description"),
                    skip_product_check=p.get("skip_product_check", False),
                    env_vars=env_vars,
                )
                config.profiles.append(profile)

            config.has_prompted_setup = data.get("has_prompted_setup", False)
        except (json.JSONDecodeError, IOError) as e:
            print(f"Warning: Failed to load config from {CONFIG_FILE}: {e}", file=sys.stderr)

    # 2. 合并环境变量配置（TEST_ES_PROFILES 等）
    _load_env_profiles(config)

    # 3. 加载安全配置
    config.safety = load_safety_config()

    return config


def _load_env_profiles(config: AppConfig) -> None:
    """从环境变量加载额外配置（向后兼容）"""
    import json as _json

    for env_name in ("test", "uat", "prod"):
        key = f"{env_name.upper()}_ES_PROFILES"
        arr_json = os.environ.get(key)
        if not arr_json or arr_json == "[]":
            continue
        try:
            arr = _json.loads(arr_json)
            if not isinstance(arr, list):
                continue
            for i, v in enumerate(arr):
                if not isinstance(v, dict):
                    continue

                name = v.get("name") or f"{env_name}_{i}"
                existing = config.get_profile(name)
                if existing:
                    continue

                hosts = v.get("hosts", "")
                if not hosts:
                    continue

                env_vars = EnvVars(hosts=hosts)
                if v.get("username"):
                    env_vars.username = v.get("username")
                if v.get("password"):
                    env_vars.password = v.get("password")

                profile = ConnectionProfile(
                    name=name,
                    env=env_name,
                    description=v.get("description"),
                    skip_product_check=v.get("skip_product_check", False),
                    env_vars=env_vars,
                )
                config.profiles.append(profile)
        except json.JSONDecodeError:
            pass


def save_profiles(profiles: list[ConnectionProfile], has_prompted_setup: bool = True) -> None:
    """保存 profiles 到配置文件（保留现有安全配置）"""
    _save_config_data({"profiles": profiles, "has_prompted_setup": has_prompted_setup})


def _save_config_data(data: dict) -> None:
    """内部函数：保存配置数据到文件（保留现有配置）"""
    _ensure_config_dir()

    full_data = {
        "profiles": [],
        "has_prompted_setup": True,
    }

    if CONFIG_FILE.exists():
        try:
            with open(CONFIG_FILE, "r", encoding="utf-8") as f:
                existing_data = json.load(f)
            full_data.update(existing_data)
        except (json.JSONDecodeError, IOError):
            pass

    full_data.update(data)

    with open(CONFIG_FILE, "w", encoding="utf-8") as f:
        json.dump(full_data, f, indent=2, ensure_ascii=False)

    SETUP_COMPLETE_FLAG.touch()


def save_wizard_state(state: dict | None) -> None:
    """保存或清除引导状态"""
    if state is None:
        # 清除状态
        _save_config_data({"wizard_state": None})
    else:
        _save_config_data({"wizard_state": state})


def load_wizard_state() -> dict | None:
    """加载引导状态"""
    if not CONFIG_FILE.exists():
        return None
    try:
        with open(CONFIG_FILE, "r", encoding="utf-8") as f:
            data = json.load(f)
        return data.get("wizard_state")
    except (json.JSONDecodeError, IOError):
        return None


def need_setup() -> bool:
    """检查是否需要引导配置"""
    if not CONFIG_FILE.exists():
        return True
    if not SETUP_COMPLETE_FLAG.exists():
        return True
    # profiles 为空也需要 setup
    config = load_config()
    if not config.profiles:
        return True
    return False


def get_connection_values(profile: ConnectionProfile) -> dict:
    """获取连接的完整配置值（用于实际连接）"""
    values = {
        "hosts": profile.env_vars.hosts or "",
        "username": profile.env_vars.username or "",
        "password": profile.env_vars.password or "",
    }

    if profile.env_vars.password and not values["password"]:
        actual_password = os.environ.get(profile.env_vars.password, "")
        if actual_password:
            values["password"] = actual_password
        else:
            values["password"] = _get_password_interactive(profile.env_vars.password)

    return values


def generate_env_var_commands(profile: ConnectionProfile) -> str:
    """生成环境变量设置命令"""
    lines = []

    if profile.env_vars.hosts:
        lines.append(f'export {profile.env_vars.hosts}="http://your-es-host:9200"')
    if profile.env_vars.username:
        lines.append(f'export {profile.env_vars.username}="your_username"')
    if profile.env_vars.password:
        lines.append(f'export {profile.env_vars.password}="your_password"')

    return "\n".join(lines)


def generate_safety_env_commands(safety: SafetyConfig) -> str:
    """生成安全配置的环境变量命令"""
    lines = [
        f'export ES_READ_ONLY="{'true' if safety.read_only else 'false'}"',
        f'export ES_MAX_SEARCH_SIZE="{safety.max_search_size}"',
        f'export ES_REQUEST_TIMEOUT="{safety.request_timeout}"',
        f'export ES_CONFIRM_WRITE="{'true' if safety.confirm_write else 'false'}"',
        f'export ES_CONFIRM_DELETE="{'true' if safety.confirm_delete else 'false'}"',
        f'export ES_CONFIRM_INDEX="{'true' if safety.confirm_index else 'false'}"',
    ]

    # DDL/DML 策略
    p = safety.policy
    lines.extend([
        f'export ES_ALLOW_SEARCH="{'true' if p.allow_search else 'false'}"',
        f'export ES_ALLOW_INDEX="{'true' if p.allow_index else 'false'}"',
        f'export ES_ALLOW_UPDATE="{'true' if p.allow_update else 'false'}"',
        f'export ES_ALLOW_DELETE="{'true' if p.allow_delete else 'false'}"',
        f'export ES_ALLOW_BULK="{'true' if p.allow_bulk else 'false'}"',
        f'export ES_ALLOW_CREATE_INDEX="{'true' if p.allow_create_index else 'false'}"',
        f'export ES_ALLOW_DELETE_INDEX="{'true' if p.allow_delete_index else 'false'}"',
        f'export ES_ALLOW_UPDATE_MAPPING="{'true' if p.allow_update_mapping else 'false'}"',
        f'export ES_ALLOW_CLUSTER="{'true' if p.allow_cluster else 'false'}"',
    ])

    return "\n".join(lines)
