from __future__ import annotations
from server.chat import chat_bp
from server.chat.settings import _dispatch_runtime_mode_notice
import json, time

PERMISSION_MODE_OPTIONS = ["readonly", "approval", "auto_approval", "unrestricted"]
EXECUTION_MODE_OPTIONS = ["sandbox", "direct"]
NETWORK_PERMISSION_OPTIONS = ["restricted", "full", "none"]
from datetime import datetime
from typing import Dict, Any, Optional
from pathlib import Path
from io import BytesIO
import zipfile
import os

from flask import Blueprint, jsonify, request, session, send_file
from werkzeug.utils import secure_filename
from werkzeug.exceptions import RequestEntityTooLarge
import secrets

from config import THINKING_FAST_INTERVAL, MAX_UPLOAD_SIZE, OUTPUT_FORMATS
from modules.personalization_manager import (
    load_personalization_config,
    resolve_context_compression_settings,
    save_personalization_config,
    THINKING_INTERVAL_MIN,
    THINKING_INTERVAL_MAX,
    RECENT_CONVERSATIONS_PROMPT_LIMIT_MIN,
    RECENT_CONVERSATIONS_PROMPT_LIMIT_MAX,
)
from modules.skills_manager import (
    get_skills_catalog,
    infer_private_skills_dir,
    merge_enabled_skills,
    sync_workspace_skills,
)
from modules.upload_security import UploadSecurityError
from modules.host_sandbox_policy import (
    load_policy,
    save_policy,
    get_macos_deny_read_paths,
    get_macos_deny_read_regexes,
)
from modules.user_manager import UserWorkspace
from core.web_terminal import WebTerminal
from config.model_profiles import get_model_context_window

from server.auth_helpers import api_login_required, resolve_admin_policy, get_current_user_record, get_current_username
from server.context import with_terminal, get_gui_manager, get_upload_guard, build_upload_error_response, ensure_conversation_loaded, get_or_create_usage_tracker
from server.security import rate_limited, prune_socket_tokens
from server.utils_common import debug_log
from server.state import PROJECT_MAX_STORAGE_MB, THINKING_FAILURE_KEYWORDS, pending_socket_tokens, SOCKET_TOKEN_TTL_SECONDS
from server.state import tool_approval_manager, user_question_manager
from server.extensions import socketio
from server.monitor import get_cached_monitor_snapshot
from server.files import sanitize_filename_preserve_unicode
import os
import re

UPLOAD_FOLDER_NAME = ".astrion/user_upload"


def _path_conflicts_with_deny_list(path: str) -> Optional[str]:
    """检查用户授权路径是否与内置 deny 列表冲突，返回错误信息或 None。"""
    if not path:
        return None
    expanded = os.path.abspath(os.path.expanduser(path))
    for deny_path in get_macos_deny_read_paths():
        deny_expanded = os.path.abspath(os.path.expanduser(deny_path))
        deny_lower = deny_expanded.lower().rstrip("/")
        expanded_lower = expanded.lower().rstrip("/")
        if expanded_lower == deny_lower or expanded_lower.startswith(deny_lower + "/"):
            return f"禁止授权敏感路径: {path}"
    for pattern in get_macos_deny_read_regexes():
        try:
            if re.search(pattern, expanded) or re.search(pattern, path):
                return f"禁止授权敏感文件: {path}"
        except re.error:
            continue
    return None
@chat_bp.route('/api/permission-mode', methods=['GET'])
@api_login_required
@with_terminal
def get_permission_mode(terminal: WebTerminal, workspace: UserWorkspace, username: str):
    """获取当前权限模式。"""
    current_conversation_id = getattr(terminal.context_manager, "current_conversation_id", None)
    return jsonify({
        "success": True,
        "mode": terminal.get_permission_mode() if hasattr(terminal, "get_permission_mode") else "unrestricted",
        "pending_mode": (terminal.get_pending_runtime_modes().get("permission_mode") if hasattr(terminal, "get_pending_runtime_modes") else None),
        "options": PERMISSION_MODE_OPTIONS,
        "conversation_id": current_conversation_id,
    })

@chat_bp.route('/api/permission-mode', methods=['POST'])
@api_login_required
@with_terminal
@rate_limited("permission_mode_switch", 30, 60, scope="user")
def update_permission_mode(terminal: WebTerminal, workspace: UserWorkspace, username: str):
    """更新当前对话权限模式。"""
    data = request.get_json() or {}
    target_mode = str(data.get("mode") or "").strip().lower()
    if target_mode not in PERMISSION_MODE_OPTIONS:
        return jsonify({
            "success": False,
            "error": "无效权限模式，仅支持 readonly / approval / auto_approval / unrestricted"
        }), 400

    # 判断当前是否在对话运行期间
    is_running = False
    try:
        from server.tasks import task_manager
        current_conv = getattr(getattr(terminal, "context_manager", None), "current_conversation_id", None)
        running_tasks = [
            r for r in task_manager.list_tasks(username) if r.status in {"pending", "running", "cancel_requested"}
        ]
        if current_conv:
            running_tasks.sort(key=lambda r: 0 if r.conversation_id == current_conv else 1)
        is_running = bool(running_tasks)
    except Exception:
        pass

    if is_running:
        # 运行期间：使用 pending 机制延迟到工具循环中统一处理，
        # 避免「A→B→A」来回切换时中间值被覆盖后仍插入多余消息。
        try:
            terminal.queue_permission_mode_change(target_mode)
        except Exception as exc:
            return jsonify({
                "success": False,
                "error": str(exc),
                "message": "更新权限模式失败"
            }), 500
        status = terminal.get_status()
        socketio.emit('status_update', status, room=f"user_{username}")
        return jsonify({
            "success": True,
            "mode": target_mode,
            "pending_mode": target_mode,
            "options": PERMISSION_MODE_OPTIONS,
            "conversation_id": getattr(terminal.context_manager, "current_conversation_id", None),
            "message": "权限模式将在当前工具执行完成后生效",
        })

    # 空闲期间：直接生效
    previous_mode = terminal.get_permission_mode() if hasattr(terminal, "get_permission_mode") else None
    try:
        applied_mode = terminal.set_permission_mode(target_mode)
        if hasattr(terminal, "pending_permission_mode"):
            terminal.pending_permission_mode = None
        if hasattr(terminal, "_persist_runtime_mode_metadata"):
            terminal._persist_runtime_mode_metadata({
                "permission_mode": applied_mode,
                "pending_permission_mode": None,
            })
    except Exception as exc:
        return jsonify({
            "success": False,
            "error": str(exc),
            "message": "更新权限模式失败"
        }), 500

    session["permission_mode"] = applied_mode
    if applied_mode != previous_mode:
        permission_label = {
            "readonly": "只读",
            "approval": "批准",
            "auto_approval": "自动审核",
            "unrestricted": "无限制",
        }.get(applied_mode, applied_mode)
        _dispatch_runtime_mode_notice(terminal, username, f"权限模式被用户修改为 {permission_label}", source="权限变更")
    status = terminal.get_status()
    socketio.emit('status_update', status, room=f"user_{username}")
    return jsonify({
        "success": True,
        "mode": applied_mode,
        "pending_mode": None,
        "options": PERMISSION_MODE_OPTIONS,
        "conversation_id": getattr(terminal.context_manager, "current_conversation_id", None),
        "message": "权限模式已更新并立即生效",
    })

@chat_bp.route('/api/execution-mode', methods=['GET'])
@api_login_required
@with_terminal
def get_execution_mode(terminal: WebTerminal, workspace: UserWorkspace, username: str):
    is_host = bool(getattr(terminal, "_is_host_mode", lambda: False)())
    can_manage = is_host and getattr(terminal, "user_role", "user") == "admin"
    state = terminal.get_execution_mode_state() if hasattr(terminal, "get_execution_mode_state") else {"mode": "sandbox"}
    return jsonify({
        "success": True,
        "enabled": can_manage,
        "state": state,
        "pending_mode": (terminal.get_pending_runtime_modes().get("execution_mode") if hasattr(terminal, "get_pending_runtime_modes") else None),
        "options": EXECUTION_MODE_OPTIONS,
    })

@chat_bp.route('/api/execution-mode', methods=['POST'])
@api_login_required
@with_terminal
@rate_limited("execution_mode_switch", 20, 60, scope="user")
def update_execution_mode(terminal: WebTerminal, workspace: UserWorkspace, username: str):
    is_host = bool(getattr(terminal, "_is_host_mode", lambda: False)())
    can_manage = is_host and getattr(terminal, "user_role", "user") == "admin"
    if not can_manage:
        return jsonify({"success": False, "error": "仅宿主机管理员可切换执行环境"}), 403
    data = request.get_json() or {}
    target_mode = str(data.get("mode") or "").strip().lower()
    if target_mode not in EXECUTION_MODE_OPTIONS:
        return jsonify({"success": False, "error": "无效执行环境，仅支持 sandbox / direct"}), 400

    # 判断当前是否在对话运行期间
    is_running = False
    try:
        from server.tasks import task_manager
        current_conv = getattr(getattr(terminal, "context_manager", None), "current_conversation_id", None)
        running_tasks = [
            r for r in task_manager.list_tasks(username) if r.status in {"pending", "running", "cancel_requested"}
        ]
        if current_conv:
            running_tasks.sort(key=lambda r: 0 if r.conversation_id == current_conv else 1)
        is_running = bool(running_tasks)
    except Exception:
        pass

    if is_running:
        # 运行期间：使用 pending 机制延迟到工具循环中统一处理，
        # 避免「A→B→A」来回切换时中间值被覆盖后仍插入多余消息。
        try:
            terminal.queue_execution_mode_change(target_mode)
        except Exception as exc:
            return jsonify({"success": False, "error": str(exc), "message": "更新执行环境失败"}), 500
        status = terminal.get_status()
        socketio.emit('status_update', status, room=f"user_{username}")
        return jsonify({
            "success": True,
            "state": {
                **(terminal.get_execution_mode_state() if hasattr(terminal, "get_execution_mode_state") else {"mode": target_mode}),
                "mode": target_mode,
            },
            "pending_mode": target_mode,
            "options": EXECUTION_MODE_OPTIONS,
            "message": "执行环境将在当前工具执行完成后生效",
        })

    # 空闲期间：直接生效
    previous_mode = terminal.get_execution_mode() if hasattr(terminal, "get_execution_mode") else None
    try:
        state = terminal.set_execution_mode(target_mode)
        if hasattr(terminal, "pending_execution_mode"):
            terminal.pending_execution_mode = None
        if hasattr(terminal, "_persist_runtime_mode_metadata"):
            terminal._persist_runtime_mode_metadata({
                "execution_mode": state.get("mode", target_mode),
                "pending_execution_mode": None,
            })
    except Exception as exc:
        return jsonify({"success": False, "error": str(exc), "message": "更新执行环境失败"}), 500
    status = terminal.get_status()
    current_mode = state.get("mode", target_mode)
    if current_mode != previous_mode:
        execution_label = {"sandbox": "沙箱", "direct": "完全访问权限"}.get(current_mode, current_mode)
        _dispatch_runtime_mode_notice(terminal, username, f"执行环境被用户修改为 {execution_label}", source="执行环境变更")
    socketio.emit('status_update', status, room=f"user_{username}")
    return jsonify({
        "success": True,
        "state": state,
        "pending_mode": None,
        "options": EXECUTION_MODE_OPTIONS,
        "message": "执行环境已更新并立即生效",
    })

@chat_bp.route('/api/network-permission', methods=['GET'])
@api_login_required
@with_terminal
def get_network_permission(terminal: WebTerminal, workspace: UserWorkspace, username: str):
    is_host = bool(getattr(terminal, "_is_host_mode", lambda: False)())
    can_manage = is_host and getattr(terminal, "user_role", "user") == "admin"
    current = terminal.get_network_permission() if hasattr(terminal, "get_network_permission") else "restricted"
    return jsonify({
        "success": True,
        "enabled": can_manage,
        "mode": current,
        "pending_mode": (terminal.get_pending_runtime_modes().get("network_permission") if hasattr(terminal, "get_pending_runtime_modes") else None),
        "options": NETWORK_PERMISSION_OPTIONS,
    })

@chat_bp.route('/api/network-permission', methods=['POST'])
@api_login_required
@with_terminal
@rate_limited("network_permission_switch", 20, 60, scope="user")
def update_network_permission(terminal: WebTerminal, workspace: UserWorkspace, username: str):
    is_host = bool(getattr(terminal, "_is_host_mode", lambda: False)())
    can_manage = is_host and getattr(terminal, "user_role", "user") == "admin"
    if not can_manage:
        return jsonify({"success": False, "error": "仅宿主机管理员可切换网络权限"}), 403
    data = request.get_json() or {}
    target_mode = str(data.get("mode") or "").strip().lower()
    if target_mode not in NETWORK_PERMISSION_OPTIONS:
        return jsonify({"success": False, "error": "无效网络权限，仅支持 restricted / full"}), 400

    is_running = False
    try:
        from server.tasks import task_manager
        current_conv = getattr(getattr(terminal, "context_manager", None), "current_conversation_id", None)
        running_tasks = [
            r for r in task_manager.list_tasks(username) if r.status in {"pending", "running", "cancel_requested"}
        ]
        if current_conv:
            running_tasks.sort(key=lambda r: 0 if r.conversation_id == current_conv else 1)
        is_running = bool(running_tasks)
    except Exception:
        pass

    if is_running:
        try:
            terminal.queue_network_permission_change(target_mode)
        except Exception as exc:
            return jsonify({"success": False, "error": str(exc), "message": "更新网络权限失败"}), 500
        status = terminal.get_status()
        socketio.emit('status_update', status, room=f"user_{username}")
        return jsonify({
            "success": True,
            "mode": target_mode,
            "pending_mode": target_mode,
            "options": NETWORK_PERMISSION_OPTIONS,
            "message": "网络权限将在当前工具执行完成后生效",
        })

    previous_mode = terminal.get_network_permission() if hasattr(terminal, "get_network_permission") else None
    try:
        applied = terminal.set_network_permission(target_mode)
        if hasattr(terminal, "pending_network_permission"):
            terminal.pending_network_permission = None
        if hasattr(terminal, "_persist_runtime_mode_metadata"):
            terminal._persist_runtime_mode_metadata({
                "network_permission": applied,
                "pending_network_permission": None,
            })
    except Exception as exc:
        return jsonify({"success": False, "error": str(exc), "message": "更新网络权限失败"}), 500
    status = terminal.get_status()
    if applied != previous_mode:
        label = {"restricted": "受限", "full": "完全开放", "none": "完全禁止"}.get(applied, applied)
        _dispatch_runtime_mode_notice(terminal, username, f"网络权限被用户修改为 {label}", source="网络权限变更")
    socketio.emit('status_update', status, room=f"user_{username}")
    return jsonify({
        "success": True,
        "mode": applied,
        "pending_mode": None,
        "options": NETWORK_PERMISSION_OPTIONS,
        "message": "网络权限已更新并立即生效",
    })

@chat_bp.route('/api/path-authorization', methods=['GET'])
@api_login_required
@with_terminal
def get_path_authorization(terminal: WebTerminal, workspace: UserWorkspace, username: str):
    is_host = bool(getattr(terminal, "_is_host_mode", lambda: False)())
    can_manage = is_host and getattr(terminal, "user_role", "user") == "admin"
    data = load_policy()
    return jsonify({
        "success": True,
        "enabled": can_manage,
        "writable_paths": data.get("macos_writable_paths", []),
        "readable_extra_paths": data.get("macos_readable_extra_paths", []),
        "deny_read_paths": data.get("macos_deny_read_paths", []),
        "deny_read_regexes": data.get("macos_deny_read_regexes", []),
    })

@chat_bp.route('/api/path-authorization', methods=['POST'])
@api_login_required
@with_terminal
@rate_limited("path_authorization_update", 20, 60, scope="user")
def update_path_authorization(terminal: WebTerminal, workspace: UserWorkspace, username: str):
    is_host = bool(getattr(terminal, "_is_host_mode", lambda: False)())
    can_manage = is_host and getattr(terminal, "user_role", "user") == "admin"
    if not can_manage:
        return jsonify({"success": False, "error": "仅宿主机管理员可管理路径授权"}), 403
    data = request.get_json() or {}
    writable_items = data.get("writable_paths")
    readable_items = data.get("readable_extra_paths")
    if not isinstance(writable_items, list) or not isinstance(readable_items, list):
        return jsonify({"success": False, "error": "writable_paths/readable_extra_paths 必须为数组"}), 400
    writable = [str(x).strip() for x in writable_items if str(x).strip()]
    readable_extra = [str(x).strip() for x in readable_items if str(x).strip()]
    if "/" in writable or "/" in readable_extra:
        return jsonify({"success": False, "error": "禁止授权根目录 /"}), 400
    for p in writable + readable_extra:
        conflict = _path_conflicts_with_deny_list(p)
        if conflict:
            return jsonify({"success": False, "error": conflict}), 400
    payload = save_policy({
        "macos_writable_paths": writable,
        "macos_readable_extra_paths": readable_extra
    })
    return jsonify({
        "success": True,
        "writable_paths": payload.get("macos_writable_paths", []),
        "readable_extra_paths": payload.get("macos_readable_extra_paths", []),
        "deny_read_paths": payload.get("macos_deny_read_paths", []),
        "deny_read_regexes": payload.get("macos_deny_read_regexes", []),
    })
