# -*- coding: utf-8 -*-
"""One-shot config migrations."""

from .shared import *
from .config_files import generate_start_config, read_config_file, write_config_file
from .discovery import get_server_list
from .project_config import get_server_manager_config_dir

_MIGRATIONS = [
    # 2026-04 版本：去掉 file:set_cwd，改由 sys.config 里 log_dir 前缀 run/<server_dir>/
    # 收敛日志，因此需要为旧服务器重新生成 sys.config。
    'sys_config_log_dir_v2',
    # 2026-04 版本：模板新增 ${role_log} 占位符，需要为所有已有服务器重新生成
    # sys.config，让 role_log 替换为 run/<server_dir>/log。
    'sys_config_role_log_v1',
    'sys_config_logger_no_domain_v1',
]


def _get_migrations_state_path(server_root) -> Path:
    """迁移状态文件路径：随项目一起存放在 .server_manager/migrations.json。"""
    return get_server_manager_config_dir(server_root).parent / 'migrations.json'


def _load_migrations_state(server_root) -> Dict[str, Any]:
    path = _get_migrations_state_path(server_root)
    if not path.exists():
        return {}
    try:
        import json
        with open(path, 'r', encoding='utf-8') as f:
            data = json.load(f)
        return data if isinstance(data, dict) else {}
    except Exception:
        return {}


def _save_migrations_state(server_root, state: Dict[str, Any]) -> None:
    path = _get_migrations_state_path(server_root)
    try:
        import json
        path.parent.mkdir(parents=True, exist_ok=True)
        with open(path, 'w', encoding='utf-8') as f:
            json.dump(state, f, ensure_ascii=False, indent=2)
    except Exception:
        pass


def _regenerate_all_sys_configs(server_root) -> Tuple[int, int, List[str]]:
    """为 run/ 下所有带 kv.config 的服务器重新生成 sys.config。
    
    Returns:
        (成功数, 失败数, 失败的服务器名列表)
    """
    run_dir = Path(server_root) / 'run'
    if not run_dir.is_dir():
        return (0, 0, [])
    
    ok, fail = 0, 0
    failed_servers: List[str] = []
    for item in sorted(run_dir.iterdir()):
        if not item.is_dir():
            continue
        if not (item / 'config' / 'kv.config').exists():
            continue
        try:
            result = generate_start_config(str(server_root), item.name)
            if result is None:
                fail += 1
                failed_servers.append(item.name)
            else:
                ok += 1
        except Exception:
            fail += 1
            failed_servers.append(item.name)
    return (ok, fail, failed_servers)


def _migrate_sys_config_log_dir_v2(server_root) -> Tuple[int, int, List[str]]:
    """为所有带 kv.config 的服务器重新生成 sys.config。
    
    对应改动：去掉启动脚本里的 file:set_cwd，改为在生成 sys.config 时把日志
    路径拼上 run/<server_dir>/，让日志仍然收敛到具体服务目录下。旧版本生成的
    sys.config 里日志路径是相对 run/<server_dir>/ 的（如 log/info.log），当前
    cwd 变为 server_root 后会写到错误位置，因此必须重新生成。
    """
    return _regenerate_all_sys_configs(server_root)


def _migrate_sys_config_role_log_v1(server_root) -> Tuple[int, int, List[str]]:
    """为所有带 kv.config 的服务器重新生成 sys.config。
    
    对应改动：sys_game.config.example 新增 ${role_log} 占位符，游戏服旧版
    sys.config 里还没有 role_log 字段；generate_start_config 会根据当前
    default.config 里的 role_log 自动计算出 run/<server_dir>/log 并替换。
    """
    return _regenerate_all_sys_configs(server_root)


def _migrate_sys_config_logger_no_domain_v1(server_root) -> Tuple[int, int, List[str]]:
    """Regenerate sys.config so existing servers use no_domain logger filters."""
    return _regenerate_all_sys_configs(server_root)


# 迁移 id -> 执行函数。函数返回 True 表示迁移执行（即使个别服务器失败也算已执行）。
_MIGRATION_RUNNERS = {
    'sys_config_log_dir_v2': _migrate_sys_config_log_dir_v2,
    'sys_config_role_log_v1': _migrate_sys_config_role_log_v1,
    'sys_config_logger_no_domain_v1': _migrate_sys_config_logger_no_domain_v1,
}


def run_startup_migrations(server_root, logger_func=None) -> Dict[str, Any]:
    """执行尚未完成的一次性迁移任务（在项目打开时调用，幂等）。
    
    状态保存在项目下 .server_manager/migrations.json，每个迁移 id 只跑一次。
    
    Args:
        server_root: 服务器根目录
        logger_func: 可选的日志回调，签名 logger_func(msg: str)，用于把进度
            输出到 GUI 控制台或 CLI，默认只 print。
    
    Returns:
        本次实际执行的迁移结果字典，形如
        {'sys_config_log_dir_v2': {'ok': 3, 'fail': 0, 'failed_servers': []}}；
        若没有需要执行的迁移则返回空字典。
    """
    if not server_root:
        return {}
    server_root = str(server_root)
    
    def _log(msg: str) -> None:
        if logger_func is not None:
            try:
                # GUI/CLI 回调约定：自带换行，保证在 OutputConsole 里逐行显示
                logger_func(msg + '\n')
                return
            except Exception:
                pass
        print(msg)
    
    state = _load_migrations_state(server_root)
    executed: Dict[str, Any] = {}
    
    for mig_id in _MIGRATIONS:
        if state.get(mig_id, {}).get('done'):
            continue
        runner = _MIGRATION_RUNNERS.get(mig_id)
        if runner is None:
            continue
        _log(f"[迁移] 开始执行: {mig_id}")
        try:
            ok, fail, failed_servers = runner(server_root)
        except Exception as e:
            _log(f"[迁移][错误] {mig_id} 执行异常: {e}")
            continue
        
        from datetime import datetime
        state[mig_id] = {
            'done': True,
            'done_at': datetime.now().isoformat(timespec='seconds'),
            'ok': ok,
            'fail': fail,
        }
        executed[mig_id] = {'ok': ok, 'fail': fail, 'failed_servers': failed_servers}
        
        if fail > 0:
            _log(f"[迁移] {mig_id} 完成: 成功 {ok} 个, 失败 {fail} 个({', '.join(failed_servers)})")
        elif ok > 0:
            _log(f"[迁移] {mig_id} 完成: 已重新生成 {ok} 个服务器的 sys.config")
        else:
            _log(f"[迁移] {mig_id} 完成: 无服务器需要处理")
    
    if executed:
        _save_migrations_state(server_root, state)
    return executed
