"""用户终端与工作区相关的共享辅助函数。"""
from __future__ import annotations
from functools import wraps
from typing import Optional, Tuple, Dict, Any
from flask import session, jsonify, has_request_context

from core.web_terminal import WebTerminal
from modules.gui_file_manager import GuiFileManager
from modules.upload_security import UploadQuarantineManager, UploadSecurityError
from modules.personalization_manager import load_personalization_config
from modules.skills_manager import infer_private_skills_dir, sync_workspace_skills
from modules.host_workspace_manager import resolve_host_workspace
from utils.host_workspace_debug import write_host_workspace_debug
import json
from pathlib import Path
from modules.usage_tracker import UsageTracker
from config import (
    DATA_DIR,
    LOGS_DIR,
    TERMINAL_SANDBOX_MODE,
    UPLOAD_QUARANTINE_SUBDIR,
)
from config.model_profiles import get_registered_model_keys

from . import state
from .utils_common import debug_log
from .auth_helpers import get_current_username, get_current_user_record, get_current_user_role  # will create helper module


def make_terminal_callback(username: str):
    """生成面向指定用户的广播函数"""
    from .extensions import socketio
    def _callback(event_type, data):
        try:
            socketio.emit(event_type, data, room=f"user_{username}")
        except Exception as exc:
            debug_log(f"广播事件失败 ({username}): {event_type} - {exc}")
    return _callback


def attach_user_broadcast(terminal: WebTerminal, username: str):
    """确保终端的广播函数指向当前用户的房间"""
    callback = make_terminal_callback(username)
    terminal.message_callback = callback
    if terminal.terminal_manager:
        terminal.terminal_manager.broadcast = callback


def _make_terminal_key(username: str, workspace_id: Optional[str] = None) -> str:
    return f"{username}::{workspace_id}" if workspace_id else username


def _set_terminal_workspace_label(terminal: WebTerminal, label: Optional[str]) -> None:
    label_text = str(label or "").strip()
    try:
        terminal.workspace_label = label_text
    except Exception:
        pass
    try:
        if getattr(terminal, "context_manager", None):
            terminal.context_manager.workspace_label = label_text
    except Exception:
        pass


def _apply_workspace_personalization_preferences(terminal: WebTerminal, workspace, update_session: bool = True) -> None:
    """Apply persisted workspace personalization after policy/workspace resolution."""
    try:
        config = load_personalization_config(workspace.data_dir)
        session_model = None
        if has_request_context():
            raw_session_model = session.get("model_key")
            if isinstance(raw_session_model, str) and raw_session_model.strip():
                session_model = raw_session_model.strip()

        # default_model 是“新会话初始偏好”，不能在每次 /api/status、任务创建、
        # 加载资源时覆盖用户已经在当前会话里手动切换的模型。
        if session_model and session_model != getattr(terminal, "model_key", None):
            try:
                terminal.set_model(session_model)
            except Exception as exc:
                debug_log(f"[Personalization] 恢复会话模型失败: {session_model} ({exc})")

        apply_default_model = not bool(session_model) and not bool(
            getattr(terminal, "_workspace_default_model_applied", False)
        )
        terminal.apply_personalization_preferences(config, apply_default_model=apply_default_model)
        if apply_default_model:
            try:
                terminal._workspace_default_model_applied = True
            except Exception:
                pass
        if has_request_context() and update_session:
            session["run_mode"] = getattr(terminal, "run_mode", session.get("run_mode"))
            session["thinking_mode"] = getattr(terminal, "thinking_mode", session.get("thinking_mode"))
            session["model_key"] = getattr(terminal, "model_key", session.get("model_key"))
    except Exception as exc:
        debug_log(f"[Personalization] 应用工作区偏好失败: {exc}")


def _ensure_workspace_skills_synced(terminal: WebTerminal, workspace) -> None:
    """
    确保工作区 skills 已按当前个性化配置完成同步。
    使用终端实例上的路径标记避免每次请求都重复全量拷贝。
    """
    try:
        project_path = str(Path(workspace.project_path).resolve())
    except Exception:
        project_path = str(workspace.project_path)

    if getattr(terminal, "_skills_synced_project_path", None) == project_path:
        return

    try:
        config = load_personalization_config(workspace.data_dir)
        enabled_skills = config.get("enabled_skills") if isinstance(config, dict) else None
        result = sync_workspace_skills(
            workspace.project_path,
            enabled_skills,
            private_dir=infer_private_skills_dir(workspace.data_dir),
        )
        if not result.get("success"):
            debug_log(f"[Skills] 工作区同步失败: {result.get('error')}")
            return
        terminal._skills_synced_project_path = project_path
        debug_log(f"[Skills] 工作区技能已同步: {project_path} ({result.get('copied', 0)} 项)")
    except Exception as exc:
        debug_log(f"[Skills] 工作区同步异常: {exc}")


def get_user_resources(
    username: Optional[str] = None,
    workspace_id: Optional[str] = None,
    update_session: bool = True,
) -> Tuple[Optional[WebTerminal], Optional['modules.user_manager.UserWorkspace']]:
    from modules.user_manager import UserWorkspace
    username = (username or get_current_username())
    if not username:
        return None, None

    # 宿主机免登录模式：根据 host_workspaces.json 选择路径，不创建 /users/<user>/project
    host_mode_session = bool(session.get("host_mode")) if has_request_context() else False
    sandbox_is_host = (TERMINAL_SANDBOX_MODE or "host").lower() == "host"
    if host_mode_session and sandbox_is_host:
        # 宿主机多工作区并行：资源选择必须优先由显式 workspace_id / 当前请求 session 决定，
        # 不能被进程级 HOST_ACTIVE_WORKSPACE_ID 覆盖，否则后台任务会在用户切换视图后串到新工作区。
        selected_workspace_id = (
            workspace_id
            or (session.get("host_workspace_id") if has_request_context() else None)
            or (session.get("workspace_id") if has_request_context() else None)
        )
        with state.HOST_ACTIVE_WORKSPACE_LOCK:
            active_workspace_id = state.HOST_ACTIVE_WORKSPACE_ID
            active_workspace_path = state.HOST_ACTIVE_WORKSPACE_PATH
            active_workspace_version = state.HOST_ACTIVE_WORKSPACE_VERSION
        if not selected_workspace_id and active_workspace_id:
            selected_workspace_id = active_workspace_id
        _, host_workspace = resolve_host_workspace(selected_workspace_id)
        if (
            active_workspace_id
            and active_workspace_path
            and selected_workspace_id == active_workspace_id
        ):
            host_workspace = dict(host_workspace)
            host_workspace["workspace_id"] = active_workspace_id
            host_workspace["path"] = active_workspace_path
        project_path = Path(host_workspace.get("path") or "").expanduser().resolve()
        write_host_workspace_debug(
            "context.get_user_resources.host.selected_workspace",
            selected_workspace_id=selected_workspace_id,
            active_workspace_id=active_workspace_id,
            active_workspace_path=active_workspace_path,
            active_workspace_version=active_workspace_version,
            resolved_workspace_id=host_workspace.get("workspace_id"),
            project_path=str(project_path),
            username=username,
        )
        project_path.mkdir(parents=True, exist_ok=True)
        data_dir = Path(DATA_DIR).expanduser().resolve()
        data_dir.mkdir(parents=True, exist_ok=True)
        logs_dir = Path(LOGS_DIR).expanduser().resolve()
        logs_dir.mkdir(parents=True, exist_ok=True)
        uploads_dir = project_path / ".astrion" / "user_upload"
        uploads_dir.mkdir(parents=True, exist_ok=True)
        skills_dir = project_path / ".astrion" / "skills"
        skills_dir.mkdir(parents=True, exist_ok=True)
        quarantine_root = Path(UPLOAD_QUARANTINE_SUBDIR).expanduser()
        if not quarantine_root.is_absolute():
            quarantine_root = (project_path.parent / UPLOAD_QUARANTINE_SUBDIR).resolve()
        quarantine_root.mkdir(parents=True, exist_ok=True)

        workspace = UserWorkspace(
            username="host",
            root=project_path.parent,
            project_path=project_path,
            data_dir=data_dir,
            logs_dir=logs_dir,
            uploads_dir=uploads_dir,
            quarantine_dir=quarantine_root,
        )
        if not hasattr(workspace, "workspace_id"):
            workspace.workspace_id = host_workspace.get("workspace_id") or "default"

        workspace_id_value = getattr(workspace, "workspace_id", None) or host_workspace.get("workspace_id") or "default"
        term_key = _make_terminal_key("host", workspace_id_value)
        container_handle = state.container_manager.ensure_container("host", str(project_path), container_key=term_key, preferred_mode="host")
        usage_tracker = None  # 宿主机模式不计配额
        terminal = state.user_terminals.get(term_key)
        target_project_path = str(project_path)
        if terminal:
            should_recreate_terminal = False
            try:
                current_project_path = str(Path(getattr(terminal, "project_path", "")).expanduser().resolve())
            except Exception:
                current_project_path = str(getattr(terminal, "project_path", ""))
            try:
                current_context_project_path = str(
                    Path(getattr(getattr(terminal, "context_manager", None), "project_path", "")).expanduser().resolve()
                )
            except Exception:
                current_context_project_path = str(
                    getattr(getattr(terminal, "context_manager", None), "project_path", "")
                )
            if current_project_path != target_project_path or current_context_project_path != target_project_path:
                write_host_workspace_debug(
                    "context.get_user_resources.host.path_mismatch",
                    terminal_id=id(terminal),
                    current_project_path=current_project_path,
                    current_context_project_path=current_context_project_path,
                    target_project_path=target_project_path,
                )
                try:
                    if hasattr(terminal, "update_project_path"):
                        terminal.update_project_path(target_project_path)
                    else:
                        should_recreate_terminal = True
                except Exception as exc:
                    debug_log(f"[HostWorkspace] update_project_path 失败，回退重建终端: {exc}")
                    should_recreate_terminal = True

                if not should_recreate_terminal:
                    try:
                        updated_project_path = str(Path(getattr(terminal, "project_path", "")).expanduser().resolve())
                    except Exception:
                        updated_project_path = str(getattr(terminal, "project_path", ""))
                    if updated_project_path != target_project_path:
                        should_recreate_terminal = True

                if should_recreate_terminal:
                    write_host_workspace_debug(
                        "context.get_user_resources.host.recreate_terminal",
                        terminal_id=id(terminal),
                        target_project_path=target_project_path,
                    )
                    try:
                        if getattr(terminal, "terminal_manager", None):
                            terminal.terminal_manager.close_all()
                    except Exception:
                        pass
                    state.user_terminals.pop(term_key, None)
                    terminal = None
        if not terminal:
            run_mode = session.get('run_mode') if has_request_context() else None
            thinking_mode_flag = session.get('thinking_mode') if has_request_context() else None
            if run_mode not in {"fast", "thinking", "deep"}:
                run_mode = "fast"
                thinking_mode_flag = False
            thinking_mode = bool(thinking_mode_flag) if thinking_mode_flag is not None else (run_mode != "fast")
            terminal = WebTerminal(
                project_path=str(project_path),
                thinking_mode=thinking_mode,
                run_mode=run_mode,
                message_callback=make_terminal_callback("host"),
                data_dir=str(data_dir),
                container_session=container_handle,
                usage_tracker=usage_tracker
            )
            if terminal.terminal_manager:
                terminal.terminal_manager.broadcast = terminal.message_callback
            state.user_terminals[term_key] = terminal
            terminal.username = "host"
            terminal.user_role = "admin"
            terminal.quota_update_callback = None
            if has_request_context() and update_session:
                session['run_mode'] = terminal.run_mode
                session['thinking_mode'] = terminal.thinking_mode
                session['workspace_id'] = getattr(workspace, "workspace_id", None)
                session['host_workspace_id'] = getattr(workspace, "workspace_id", None)
        else:
            terminal.update_container_session(container_handle)
            attach_user_broadcast(terminal, "host")
            terminal.username = "host"
            terminal.user_role = "admin"
            if has_request_context() and update_session:
                session['workspace_id'] = getattr(workspace, "workspace_id", None)
                session['host_workspace_id'] = getattr(workspace, "workspace_id", None)
        _set_terminal_workspace_label(
            terminal,
            host_workspace.get("label") or host_workspace.get("workspace_id") or getattr(workspace, "workspace_id", None),
        )

        # 宿主机模式同样需要应用管理员策略（否则前端工具菜单会退化成静态基础分类）
        try:
            from core.tool_config import ToolCategory
            from modules import admin_policy_manager

            record = get_current_user_record()
            role = get_current_user_role(record) if record else "admin"
            invite_code = getattr(record, "invite_code", None) if record else None
            policy = admin_policy_manager.get_effective_policy(
                record.username if record else username,
                role,
                invite_code,
            )
            categories_map = {
                cid: ToolCategory(
                    label=cat.get("label") or cid,
                    tools=list(cat.get("tools") or []),
                    default_enabled=bool(cat.get("default_enabled", True)),
                    silent_when_disabled=bool(cat.get("silent_when_disabled", False)),
                )
                for cid, cat in policy.get("categories", {}).items()
            }
            forced_states = policy.get("forced_category_states") or {}
            disabled_models = policy.get("disabled_models") or []
            terminal.set_admin_policy(categories_map, forced_states, disabled_models)
            terminal.admin_policy_ui_blocks = policy.get("ui_blocks") or {}
            terminal.admin_policy_version = policy.get("updated_at")
            if terminal.model_key in disabled_models:
                for candidate in get_registered_model_keys(visible_only=True):
                    if candidate not in disabled_models:
                        try:
                            terminal.set_model(candidate)
                            if has_request_context() and update_session:
                                session["model_key"] = terminal.model_key
                            break
                        except Exception:
                            continue
        except Exception as exc:
            debug_log(f"[admin_policy][host_mode] 应用失败: {exc}")

        _apply_workspace_personalization_preferences(terminal, workspace)
        _ensure_workspace_skills_synced(terminal, workspace)
        write_host_workspace_debug(
            "context.get_user_resources.host.return",
            terminal_id=id(terminal),
            terminal_project_path=str(getattr(terminal, "project_path", "")),
            context_project_path=str(getattr(getattr(terminal, "context_manager", None), "project_path", "")),
            workspace_project_path=str(getattr(workspace, "project_path", "")),
            workspace_id=getattr(workspace, "workspace_id", None),
            current_conversation_id=getattr(getattr(terminal, "context_manager", None), "current_conversation_id", None),
        )
        return terminal, workspace

    is_api_user = bool(session.get("is_api_user")) if has_request_context() else False
    # API 用户与网页用户使用不同的 manager
    if is_api_user:
        record = None
        if workspace_id is None:
            raise RuntimeError("API 调用缺少 workspace_id")
        workspace = state.api_user_manager.ensure_workspace(username, workspace_id)
    else:
        record = get_current_user_record()
        selected_workspace_id = (
            workspace_id
            or (session.get("workspace_id") if has_request_context() else None)
            or "default"
        )
        workspace = state.user_manager.ensure_user_workspace(username, selected_workspace_id)
        # 为兼容后续逻辑，补充 workspace_id 属性
        if not hasattr(workspace, "workspace_id"):
            try:
                workspace.workspace_id = selected_workspace_id or "default"
            except Exception:
                pass
    workspace_id_value = getattr(workspace, "workspace_id", None) or "default"
    term_key = _make_terminal_key(username, workspace_id_value)
    container_handle = state.container_manager.ensure_container(username, str(workspace.project_path), container_key=term_key, preferred_mode="docker")
    usage_tracker = None if is_api_user else get_or_create_usage_tracker(username, workspace)
    terminal = state.user_terminals.get(term_key)
    if not terminal:
        run_mode = session.get('run_mode') if has_request_context() else None
        thinking_mode_flag = session.get('thinking_mode') if has_request_context() else None
        if run_mode not in {"fast", "thinking", "deep"}:
            preferred_run_mode = None
            try:
                personal_config = load_personalization_config(workspace.data_dir)
                candidate_mode = (personal_config or {}).get('default_run_mode')
                if isinstance(candidate_mode, str) and candidate_mode.lower() in {"fast", "thinking", "deep"}:
                    preferred_run_mode = candidate_mode.lower()
            except Exception as exc:
                debug_log(f"[UserInit] 加载个性化偏好失败: {exc}")

            if preferred_run_mode:
                run_mode = preferred_run_mode
                thinking_mode_flag = preferred_run_mode != "fast"
            elif thinking_mode_flag:
                run_mode = "deep"
            else:
                run_mode = "fast"
        thinking_mode = run_mode != "fast"
        terminal = WebTerminal(
            project_path=str(workspace.project_path),
            thinking_mode=thinking_mode,
            run_mode=run_mode,
            message_callback=make_terminal_callback(username),
            data_dir=str(workspace.data_dir),
            container_session=container_handle,
            usage_tracker=usage_tracker
        )
        if terminal.terminal_manager:
            terminal.terminal_manager.broadcast = terminal.message_callback
        state.user_terminals[term_key] = terminal
        terminal.username = username
        terminal.user_role = "api" if is_api_user else get_current_user_role(record)
        terminal.quota_update_callback = (lambda metric=None: emit_user_quota_update(username)) if not is_api_user else None
        if has_request_context() and update_session:
            session['run_mode'] = terminal.run_mode
            session['thinking_mode'] = terminal.thinking_mode
            session['model_key'] = getattr(terminal, "model_key", None)
            session['workspace_id'] = getattr(workspace, "workspace_id", None)
    else:
        terminal.update_container_session(container_handle)
        attach_user_broadcast(terminal, username)
        terminal.username = username
        terminal.user_role = "api" if is_api_user else get_current_user_role(record)
        terminal.quota_update_callback = (lambda metric=None: emit_user_quota_update(username)) if not is_api_user else None
        if has_request_context() and update_session:
            session['workspace_id'] = getattr(workspace, "workspace_id", None)

    if is_api_user:
        workspace_label = workspace_id_value
    else:
        try:
            workspace_label = (
                state.user_manager.list_user_workspaces(username)
                .get(workspace_id_value, {})
                .get("label")
            ) or workspace_id_value
        except Exception:
            workspace_label = workspace_id_value
    _set_terminal_workspace_label(terminal, workspace_label)

    # 应用管理员策略
    if not is_api_user:
        try:
            from core.tool_config import ToolCategory
            from modules import admin_policy_manager
            policy = admin_policy_manager.get_effective_policy(
                record.username if record else None,
                get_current_user_role(record),
                getattr(record, "invite_code", None),
            )
            categories_map = {
                cid: ToolCategory(
                    label=cat.get("label") or cid,
                    tools=list(cat.get("tools") or []),
                    default_enabled=bool(cat.get("default_enabled", True)),
                    silent_when_disabled=bool(cat.get("silent_when_disabled", False)),
                )
                for cid, cat in policy.get("categories", {}).items()
            }
            forced_states = policy.get("forced_category_states") or {}
            disabled_models = policy.get("disabled_models") or []
            terminal.set_admin_policy(categories_map, forced_states, disabled_models)
            terminal.admin_policy_ui_blocks = policy.get("ui_blocks") or {}
            terminal.admin_policy_version = policy.get("updated_at")
            if terminal.model_key in disabled_models:
                for candidate in get_registered_model_keys(visible_only=True):
                    if candidate not in disabled_models:
                        try:
                            terminal.set_model(candidate)
                            if update_session:
                                session["model_key"] = terminal.model_key
                            break
                        except Exception:
                            continue
        except Exception as exc:
            debug_log(f"[admin_policy] 应用失败: {exc}")

    _apply_workspace_personalization_preferences(terminal, workspace, update_session=update_session)
    _ensure_workspace_skills_synced(terminal, workspace)
    return terminal, workspace


def get_or_create_usage_tracker(username: Optional[str], workspace: Optional['modules.user_manager.UserWorkspace'] = None) -> Optional[UsageTracker]:
    if not username:
        return None
    tracker = state.usage_trackers.get(username)
    if tracker:
        return tracker
    from modules.user_manager import UserWorkspace
    if workspace is None:
        workspace = state.user_manager.ensure_user_workspace(username)
    record = state.user_manager.get_user(username)
    role = getattr(record, "role", "user") if record else "user"
    tracker = UsageTracker(str(workspace.data_dir), role=role or "user")
    state.usage_trackers[username] = tracker
    return tracker


def emit_user_quota_update(username: Optional[str]):
    from .extensions import socketio
    if not username:
        return
    tracker = get_or_create_usage_tracker(username)
    if not tracker:
        return
    try:
        snapshot = tracker.get_quota_snapshot()
        socketio.emit('quota_update', {'quotas': snapshot}, room=f"user_{username}")
    except Exception:
        pass


def apply_conversation_overrides(terminal: WebTerminal, workspace, conversation_id: Optional[str]):
    """根据对话元数据应用自定义 prompt / personalization（仅 API 用途）。"""
    if not conversation_id:
        return
    conv_path = Path(workspace.data_dir) / "conversations" / f"{conversation_id}.json"
    if not conv_path.exists():
        return
    try:
        data = json.loads(conv_path.read_text(encoding="utf-8"))
        meta = data.get("metadata") or {}
        prompt_name = meta.get("custom_prompt_name")
        personalization_name = meta.get("personalization_name")
        # prompt override
        if prompt_name:
            prompt_path = Path(workspace.data_dir) / "prompts" / f"{prompt_name}.txt"
            if prompt_path.exists():
                terminal.context_manager.custom_system_prompt = prompt_path.read_text(encoding="utf-8")
            else:
                terminal.context_manager.custom_system_prompt = None
        else:
            terminal.context_manager.custom_system_prompt = None
        # personalization override
        if personalization_name:
            pers_path = Path(workspace.data_dir) / "personalization" / f"{personalization_name}.json"
            if pers_path.exists():
                try:
                    terminal.context_manager.custom_personalization_config = json.loads(pers_path.read_text(encoding="utf-8"))
                except Exception:
                    terminal.context_manager.custom_personalization_config = None
            else:
                terminal.context_manager.custom_personalization_config = None
        else:
            terminal.context_manager.custom_personalization_config = None

        # 应用个性化偏好（含禁用工具分类）到当前终端
        try:
            terminal.apply_personalization_preferences(terminal.context_manager.custom_personalization_config)
        except Exception as exc:
            debug_log(f"[apply_overrides] 应用个性化失败: {exc}")
    except Exception as exc:
        debug_log(f"[apply_overrides] 读取对话元数据失败: {exc}")


def with_terminal(func):
    """注入用户专属终端和工作区"""
    @wraps(func)
    def wrapper(*args, **kwargs):
        username = get_current_username()
        try:
            terminal, workspace = get_user_resources(username)
        except RuntimeError as exc:
            return jsonify({"error": str(exc), "code": "resource_busy"}), 503
        if not terminal or not workspace:
            return jsonify({"error": "System not initialized"}), 503
        kwargs.update({
            'terminal': terminal,
            'workspace': workspace,
            'username': username
        })
        return func(*args, **kwargs)
    return wrapper


def get_terminal_for_sid(sid: str):
    username = state.connection_users.get(sid)
    if not username:
        return None, None, None
    try:
        terminal, workspace = get_user_resources(username)
    except RuntimeError:
        return username, None, None
    return username, terminal, workspace


def get_gui_manager(workspace):
    return GuiFileManager(str(workspace.project_path))


def get_upload_guard(workspace):
    return UploadQuarantineManager(workspace)


def build_upload_error_response(exc: UploadSecurityError):
    status = 400
    if exc.code in {"scanner_missing", "scanner_unavailable"}:
        status = 500
    return jsonify({
        "success": False,
        "error": str(exc),
        "code": exc.code,
    }), status


def ensure_conversation_loaded(
    terminal: WebTerminal,
    conversation_id: Optional[str],
    workspace=None,
):
    created_new = False
    if not conversation_id:
        result = terminal.create_new_conversation()
        if not result.get("success"):
            raise RuntimeError(result.get("message", "创建对话失败"))
        conversation_id = result["conversation_id"]
        if has_request_context():
            session['run_mode'] = terminal.run_mode
            session['thinking_mode'] = terminal.thinking_mode
        created_new = True
    else:
        conversation_id = conversation_id if conversation_id.startswith('conv_') else f"conv_{conversation_id}"
        current_id = terminal.context_manager.current_conversation_id
        if current_id != conversation_id:
            load_result = terminal.load_conversation(conversation_id)
            if not load_result.get("success"):
                raise RuntimeError(load_result.get("message", "对话加载失败"))
            write_host_workspace_debug(
                "context.ensure_conversation_loaded.after_load",
                terminal_id=id(terminal),
                conversation_id=conversation_id,
                terminal_project_path=str(getattr(terminal, "project_path", "")),
                context_project_path=str(getattr(getattr(terminal, "context_manager", None), "project_path", "")),
                metadata_project_path=(
                    getattr(getattr(terminal, "context_manager", None), "conversation_metadata", {}) or {}
                ).get("project_path"),
            )
            try:
                conv_data = terminal.context_manager._get_conversation_manager_for_id(conversation_id).load_conversation(conversation_id) or {}
                meta = conv_data.get("metadata", {}) or {}
                run_mode_meta = meta.get("run_mode")
                if run_mode_meta:
                    terminal.set_run_mode(run_mode_meta)
                elif meta.get("thinking_mode"):
                    terminal.set_run_mode("thinking")
                else:
                    terminal.set_run_mode("fast")
                if terminal.thinking_mode:
                    terminal.api_client.start_new_task(force_deep=terminal.deep_thinking_mode)
                else:
                    terminal.api_client.start_new_task()
                if has_request_context():
                    session['run_mode'] = terminal.run_mode
                    session['thinking_mode'] = terminal.thinking_mode
                    session['model_key'] = getattr(terminal, "model_key", None)
            except Exception:
                pass
    if workspace is not None:
        try:
            workspace_project_path = str(Path(workspace.project_path).expanduser().resolve())
            terminal.update_project_path(workspace_project_path)
            write_host_workspace_debug(
                "context.ensure_conversation_loaded.reapply_workspace_path",
                terminal_id=id(terminal),
                conversation_id=conversation_id,
                workspace_project_path=workspace_project_path,
                terminal_project_path=str(getattr(terminal, "project_path", "")),
                context_project_path=str(getattr(getattr(terminal, "context_manager", None), "project_path", "")),
            )
        except Exception as exc:
            write_host_workspace_debug(
                "context.ensure_conversation_loaded.reapply_workspace_path_failed",
                terminal_id=id(terminal),
                conversation_id=conversation_id,
                error=str(exc),
            )
    # 应用对话级自定义 prompt / personalization（仅 API）。
    # 注意：ensure_conversation_loaded 在 WebSocket/后台任务等多处复用，有些调用点拿不到 workspace；
    #       因此这里允许 workspace 为空（仅跳过 override，不影响正常对话加载）。
    if workspace is not None:
        try:
            apply_conversation_overrides(terminal, workspace, conversation_id)
        except Exception as exc:
            debug_log(f"[apply_overrides] 失败: {exc}")
    return conversation_id, created_new


def reset_system_state(terminal: Optional[WebTerminal]):
    """完整重置系统状态"""
    if not terminal:
        return
    try:
        if hasattr(terminal, 'api_client') and terminal.api_client:
            debug_log("重置API客户端状态")
            terminal.api_client.start_new_task(force_deep=getattr(terminal, "deep_thinking_mode", False))
        if hasattr(terminal, 'current_session_id'):
            terminal.current_session_id += 1
            debug_log(f"重置会话ID为: {terminal.current_session_id}")
        web_attrs = ['streamingMessage', 'currentMessageIndex', 'preparingTools', 'activeTools']
        for attr in web_attrs:
            if hasattr(terminal, attr):
                if attr in ['streamingMessage']:
                    setattr(terminal, attr, False)
                elif attr in ['currentMessageIndex']:
                    setattr(terminal, attr, -1)
                elif attr in ['preparingTools', 'activeTools'] and hasattr(getattr(terminal, attr), 'clear'):
                    getattr(terminal, attr).clear()
        debug_log("系统状态重置完成")
    except Exception as e:
        debug_log(f"状态重置过程中出现错误: {e}")
        import traceback
        debug_log(f"错误详情: {traceback.format_exc()}")


__all__ = [
    "get_user_resources",
    "with_terminal",
    "get_terminal_for_sid",
    "get_gui_manager",
    "get_upload_guard",
    "build_upload_error_response",
    "ensure_conversation_loaded",
    "reset_system_state",
    "get_or_create_usage_tracker",
    "emit_user_quota_update",
    "attach_user_broadcast",
]
