#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import annotations

import os
import posixpath
import secrets
import threading
import time
from datetime import datetime

import database
import email_service
import requests
from app_config import get_config
from app_logger import get_logger
from app_security import (
    get_rate_limit_ip,
    is_safe_outbound_url,
    is_safe_path,
    require_ip_not_locked,
    sanitize_filename,
    validate_email,
    validate_password,
)
from flask import current_app, jsonify, redirect, request, session, url_for
from routes.admin_api import admin_api_bp
from routes.decorators import admin_required
from services.accounts_service import load_user_accounts
from services.browse_types import BROWSE_TYPE_SHOULD_READ, validate_browse_type
from services.checkpoints import get_checkpoint_mgr
from services.scheduler import run_scheduled_task
from services.state import (
    safe_clear_user_logs,
    safe_get_account,
    safe_get_user_accounts_snapshot,
    safe_iter_task_status_items,
    safe_remove_user_accounts,
    safe_verify_and_consume_captcha,
    check_login_ip_user_locked,
    check_login_rate_limits,
    get_login_failure_delay_seconds,
    record_login_username_attempt,
    check_ip_request_rate,
    check_login_captcha_required,
    clear_login_failures,
    record_login_failure,
)
from services.tasks import get_task_scheduler, submit_account_task
from services.time_utils import BEIJING_TZ, get_beijing_now

logger = get_logger("app")
config = get_config()

_server_cpu_percent_lock = threading.Lock()
_server_cpu_percent_last: float | None = None
_server_cpu_percent_last_ts = 0.0


def _get_server_cpu_percent() -> float:
    import psutil

    global _server_cpu_percent_last, _server_cpu_percent_last_ts

    now = time.time()
    with _server_cpu_percent_lock:
        if _server_cpu_percent_last is not None and (now - _server_cpu_percent_last_ts) < 0.5:
            return _server_cpu_percent_last

        try:
            if _server_cpu_percent_last is None:
                cpu_percent = float(psutil.cpu_percent(interval=0.1))
            else:
                cpu_percent = float(psutil.cpu_percent(interval=None))
        except Exception:
            cpu_percent = float(_server_cpu_percent_last or 0.0)

        if cpu_percent < 0:
            cpu_percent = 0.0

        _server_cpu_percent_last = cpu_percent
        _server_cpu_percent_last_ts = now
        return cpu_percent


def _admin_reauth_required() -> bool:
    try:
        return time.time() > float(session.get("admin_reauth_until", 0) or 0)
    except Exception:
        return True


def _require_admin_reauth():
    if _admin_reauth_required():
        return jsonify({"error": "需要二次确认", "code": "reauth_required"}), 401
    return None

def _get_upload_dir():
    rel_dir = getattr(config, "ANNOUNCEMENT_IMAGE_DIR", "static/announcements")
    if not is_safe_path(current_app.root_path, rel_dir):
        rel_dir = "static/announcements"
    abs_dir = os.path.join(current_app.root_path, rel_dir)
    os.makedirs(abs_dir, exist_ok=True)
    return abs_dir, rel_dir


def _get_file_size(file_storage):
    try:
        file_storage.stream.seek(0, os.SEEK_END)
        size = file_storage.stream.tell()
        file_storage.stream.seek(0)
        return size
    except Exception:
        return None


@admin_api_bp.route("/debug-config", methods=["GET"])
@admin_required
def debug_config():
    """调试配置信息（仅管理员可访问，生产环境应禁用）"""
    if not current_app.debug:
        return jsonify({"error": "调试端点已在生产环境禁用"}), 403

    return jsonify(
        {
            "secret_key_set": bool(current_app.secret_key),
            "secret_key_length": len(current_app.secret_key) if current_app.secret_key else 0,
            "session_config": {
                "SESSION_COOKIE_NAME": current_app.config.get("SESSION_COOKIE_NAME"),
                "SESSION_COOKIE_SECURE": current_app.config.get("SESSION_COOKIE_SECURE"),
                "SESSION_COOKIE_HTTPONLY": current_app.config.get("SESSION_COOKIE_HTTPONLY"),
                "SESSION_COOKIE_SAMESITE": current_app.config.get("SESSION_COOKIE_SAMESITE"),
                "PERMANENT_SESSION_LIFETIME": str(current_app.config.get("PERMANENT_SESSION_LIFETIME")),
            },
            "has_session": bool(session),
            "cookies_received": list(request.cookies.keys()),
        }
    )


@admin_api_bp.route("/login", methods=["POST"])
@require_ip_not_locked
def admin_login():
    """管理员登录（支持JSON和form-data两种格式）"""
    if request.is_json:
        data = request.json or {}
    else:
        data = request.form

    username = data.get("username", "").strip()
    password = data.get("password", "").strip()
    captcha_session = data.get("captcha_session", "")
    captcha_code = data.get("captcha", "").strip()
    need_captcha = data.get("need_captcha", False)

    client_ip = get_rate_limit_ip()
    username_key = username

    scan_locked = record_login_username_attempt(client_ip, username_key)
    is_locked, remaining = check_login_ip_user_locked(client_ip, username_key)
    if is_locked:
        wait_hint = f"{remaining // 60 + 1}分钟" if remaining >= 60 else f"{remaining}秒"
        if request.is_json:
            return jsonify({"error": f"账号短时锁定，请{wait_hint}后再试", "need_captcha": True}), 429
        return redirect(url_for("pages.admin_login_page"))

    allowed, error_msg = check_ip_request_rate(client_ip, "login")
    if not allowed:
        if request.is_json:
            return jsonify({"error": error_msg, "need_captcha": True}), 429
        return redirect(url_for("pages.admin_login_page"))

    allowed, error_msg = check_login_rate_limits(client_ip, username_key)
    if not allowed:
        if request.is_json:
            return jsonify({"error": error_msg, "need_captcha": True}), 429
        return redirect(url_for("pages.admin_login_page"))

    captcha_required = check_login_captcha_required(client_ip, username_key) or scan_locked or bool(need_captcha)
    if captcha_required:
        if not captcha_session or not captcha_code:
            if request.is_json:
                return jsonify({"error": "请填写验证码", "need_captcha": True}), 400
            return redirect(url_for("pages.admin_login_page"))
        success, message = safe_verify_and_consume_captcha(captcha_session, captcha_code)
        if not success:
            record_login_failure(client_ip, username_key)
            if request.is_json:
                return jsonify({"error": message, "need_captcha": True}), 400
            return redirect(url_for("pages.admin_login_page"))

    admin = database.verify_admin(username, password)
    if admin:
        clear_login_failures(client_ip, username_key)
        session.pop("admin_id", None)
        session.pop("admin_username", None)
        session["admin_id"] = admin["id"]
        session["admin_username"] = admin["username"]
        session["admin_reauth_until"] = time.time() + int(config.ADMIN_REAUTH_WINDOW_SECONDS)
        session.permanent = True
        session.modified = True

        logger.info(f"[admin_login] 管理员 {username} 登录成功")

        if request.is_json:
            return jsonify({"success": True, "redirect": "/yuyx/admin"})
        return redirect(url_for("pages.admin_page"))

    record_login_failure(client_ip, username_key)
    delay = get_login_failure_delay_seconds(client_ip, username_key)
    if delay > 0:
        time.sleep(delay)
    logger.warning(f"[admin_login] 管理员 {username} 登录失败 - 用户名或密码错误")
    if request.is_json:
        return jsonify({"error": "管理员用户名或密码错误", "need_captcha": check_login_captcha_required(client_ip)}), 401
    return redirect(url_for("pages.admin_login_page"))


@admin_api_bp.route("/logout", methods=["POST"])
@admin_required
def admin_logout():
    """管理员登出"""
    session.pop("admin_id", None)
    session.pop("admin_username", None)
    session.pop("admin_reauth_until", None)
    return jsonify({"success": True})


@admin_api_bp.route("/admin/reauth", methods=["POST"])
@admin_required
def admin_reauth():
    """管理员敏感操作二次确认"""
    data = request.json or {}
    password = (data.get("password") or "").strip()
    if not password:
        return jsonify({"error": "密码不能为空"}), 400

    username = session.get("admin_username")
    if not username:
        return jsonify({"error": "未登录"}), 401

    admin = database.verify_admin(username, password)
    if not admin:
        return jsonify({"error": "密码错误"}), 401

    session["admin_reauth_until"] = time.time() + int(config.ADMIN_REAUTH_WINDOW_SECONDS)
    session.modified = True
    return jsonify({"success": True, "expires_in": int(config.ADMIN_REAUTH_WINDOW_SECONDS)})


# ==================== 公告管理API（管理员） ====================


@admin_api_bp.route("/announcements/upload_image", methods=["POST"])
@admin_required
def admin_upload_announcement_image():
    """上传公告图片（返回可访问URL）"""
    file = request.files.get("file")
    if not file or not file.filename:
        return jsonify({"error": "请选择图片"}), 400

    filename = sanitize_filename(file.filename)
    ext = os.path.splitext(filename)[1].lower()
    allowed_exts = getattr(config, "ALLOWED_ANNOUNCEMENT_IMAGE_EXTENSIONS", {".png", ".jpg", ".jpeg"})
    if not ext or ext not in allowed_exts:
        return jsonify({"error": "不支持的图片格式"}), 400
    if file.mimetype and not str(file.mimetype).startswith("image/"):
        return jsonify({"error": "文件类型无效"}), 400

    size = _get_file_size(file)
    max_size = int(getattr(config, "MAX_ANNOUNCEMENT_IMAGE_SIZE", 5 * 1024 * 1024))
    if size is not None and size > max_size:
        max_mb = max_size // 1024 // 1024
        return jsonify({"error": f"图片大小不能超过{max_mb}MB"}), 400

    abs_dir, rel_dir = _get_upload_dir()
    token = secrets.token_hex(6)
    name = f"announcement_{int(time.time())}_{token}{ext}"
    save_path = os.path.join(abs_dir, name)
    file.save(save_path)

    static_root = os.path.join(current_app.root_path, "static")
    rel_to_static = os.path.relpath(abs_dir, static_root)
    if rel_to_static.startswith(".."):
        rel_to_static = "announcements"
    url_path = posixpath.join(rel_to_static.replace(os.sep, "/"), name)
    return jsonify({"success": True, "url": url_for("serve_static", filename=url_path)})


@admin_api_bp.route("/announcements", methods=["GET"])
@admin_required
def admin_get_announcements():
    """获取公告列表"""
    try:
        limit = int(request.args.get("limit", 50))
        offset = int(request.args.get("offset", 0))
    except (TypeError, ValueError):
        limit, offset = 50, 0

    limit = max(1, min(200, limit))
    offset = max(0, offset)
    return jsonify(database.get_announcements(limit=limit, offset=offset))


@admin_api_bp.route("/announcements", methods=["POST"])
@admin_required
def admin_create_announcement():
    """创建公告（默认启用并替换旧公告）"""
    data = request.json or {}
    title = (data.get("title") or "").strip()
    content = (data.get("content") or "").strip()
    image_url = (data.get("image_url") or "").strip()
    is_active = bool(data.get("is_active", True))

    if image_url and len(image_url) > 1000:
        return jsonify({"error": "图片地址过长"}), 400

    announcement_id = database.create_announcement(title, content, image_url=image_url, is_active=is_active)
    if not announcement_id:
        return jsonify({"error": "标题和内容不能为空"}), 400

    return jsonify({"success": True, "id": announcement_id})


@admin_api_bp.route("/announcements/<int:announcement_id>/activate", methods=["POST"])
@admin_required
def admin_activate_announcement(announcement_id):
    """启用公告（会自动停用其他公告）"""
    if not database.get_announcement_by_id(announcement_id):
        return jsonify({"error": "公告不存在"}), 404
    ok = database.set_announcement_active(announcement_id, True)
    return jsonify({"success": ok})


@admin_api_bp.route("/announcements/<int:announcement_id>/deactivate", methods=["POST"])
@admin_required
def admin_deactivate_announcement(announcement_id):
    """停用公告"""
    if not database.get_announcement_by_id(announcement_id):
        return jsonify({"error": "公告不存在"}), 404
    ok = database.set_announcement_active(announcement_id, False)
    return jsonify({"success": ok})


@admin_api_bp.route("/announcements/<int:announcement_id>", methods=["DELETE"])
@admin_required
def admin_delete_announcement(announcement_id):
    """删除公告"""
    if not database.get_announcement_by_id(announcement_id):
        return jsonify({"error": "公告不存在"}), 404
    ok = database.delete_announcement(announcement_id)
    return jsonify({"success": ok})


# ==================== 用户管理/统计（管理员） ====================


@admin_api_bp.route("/users", methods=["GET"])
@admin_required
def get_all_users():
    """获取所有用户"""
    users = database.get_all_users()
    return jsonify(users)


@admin_api_bp.route("/users/pending", methods=["GET"])
@admin_required
def get_pending_users():
    """获取待审核用户"""
    users = database.get_pending_users()
    return jsonify(users)


@admin_api_bp.route("/users/<int:user_id>/approve", methods=["POST"])
@admin_required
def approve_user_route(user_id):
    """审核通过用户"""
    if database.approve_user(user_id):
        return jsonify({"success": True})
    return jsonify({"error": "审核失败"}), 400


@admin_api_bp.route("/users/<int:user_id>/reject", methods=["POST"])
@admin_required
def reject_user_route(user_id):
    """拒绝用户"""
    if database.reject_user(user_id):
        return jsonify({"success": True})
    return jsonify({"error": "拒绝失败"}), 400


@admin_api_bp.route("/users/<int:user_id>", methods=["DELETE"])
@admin_required
def delete_user_route(user_id):
    """删除用户"""
    if database.delete_user(user_id):
        safe_remove_user_accounts(user_id)
        safe_clear_user_logs(user_id)
        return jsonify({"success": True})
    return jsonify({"error": "删除失败"}), 400


@admin_api_bp.route("/stats", methods=["GET"])
@admin_required
def get_system_stats():
    """获取系统统计"""
    stats = database.get_system_stats()
    stats["admin_username"] = session.get("admin_username", "admin")
    return jsonify(stats)


@admin_api_bp.route("/browser_pool/stats", methods=["GET"])
@admin_required
def get_browser_pool_stats():
    """获取截图线程池状态"""
    try:
        from browser_pool_worker import get_browser_worker_pool

        pool = get_browser_worker_pool()
        stats = pool.get_stats() or {}

        worker_details = []
        for w in stats.get("workers") or []:
            last_ts = float(w.get("last_active_ts") or 0)
            last_active_at = None
            if last_ts > 0:
                try:
                    last_active_at = datetime.fromtimestamp(last_ts, tz=BEIJING_TZ).strftime("%Y-%m-%d %H:%M:%S")
                except Exception:
                    last_active_at = None

            created_ts = w.get("browser_created_at")
            created_at = None
            if created_ts:
                try:
                    created_at = datetime.fromtimestamp(float(created_ts), tz=BEIJING_TZ).strftime("%Y-%m-%d %H:%M:%S")
                except Exception:
                    created_at = None

            worker_details.append(
                {
                    "worker_id": w.get("worker_id"),
                    "idle": bool(w.get("idle")),
                    "has_browser": bool(w.get("has_browser")),
                    "total_tasks": int(w.get("total_tasks") or 0),
                    "failed_tasks": int(w.get("failed_tasks") or 0),
                    "browser_use_count": int(w.get("browser_use_count") or 0),
                    "browser_created_at": created_at,
                    "browser_created_ts": created_ts,
                    "last_active_at": last_active_at,
                    "last_active_ts": last_ts,
                    "thread_alive": bool(w.get("thread_alive")),
                }
            )

        total_workers = len(worker_details) if worker_details else int(stats.get("pool_size") or 0)
        return jsonify(
            {
                "total_workers": total_workers,
                "active_workers": int(stats.get("busy_workers") or 0),
                "idle_workers": int(stats.get("idle_workers") or 0),
                "queue_size": int(stats.get("queue_size") or 0),
                "workers": worker_details,
                "summary": {
                    "total_tasks": int(stats.get("total_tasks") or 0),
                    "failed_tasks": int(stats.get("failed_tasks") or 0),
                    "success_rate": stats.get("success_rate"),
                },
                "server_time_cst": get_beijing_now().strftime("%Y-%m-%d %H:%M:%S"),
            }
        )
    except Exception as e:
        logger.exception(f"[AdminAPI] 获取截图线程池状态失败: {e}")
        return jsonify({"error": "获取截图线程池状态失败"}), 500


@admin_api_bp.route("/docker_stats", methods=["GET"])
@admin_required
def get_docker_stats():
    """获取Docker容器运行状态"""
    import subprocess

    docker_status = {
        "running": False,
        "container_name": "N/A",
        "uptime": "N/A",
        "memory_usage": "N/A",
        "memory_limit": "N/A",
        "memory_percent": "N/A",
        "cpu_percent": "N/A",
        "status": "Unknown",
    }

    try:
        if os.path.exists("/.dockerenv"):
            docker_status["running"] = True

            try:
                with open("/etc/hostname", "r") as f:
                    docker_status["container_name"] = f.read().strip()
            except Exception as e:
                logger.debug(f"读取容器名称失败: {e}")

            try:
                if os.path.exists("/sys/fs/cgroup/memory.current"):
                    with open("/sys/fs/cgroup/memory.current", "r") as f:
                        mem_total = int(f.read().strip())

                    cache = 0
                    if os.path.exists("/sys/fs/cgroup/memory.stat"):
                        with open("/sys/fs/cgroup/memory.stat", "r") as f:
                            for line in f:
                                if line.startswith("inactive_file "):
                                    cache = int(line.split()[1])
                                    break

                    mem_bytes = mem_total - cache
                    docker_status["memory_usage"] = "{:.2f} MB".format(mem_bytes / 1024 / 1024)

                    if os.path.exists("/sys/fs/cgroup/memory.max"):
                        with open("/sys/fs/cgroup/memory.max", "r") as f:
                            limit_str = f.read().strip()
                            if limit_str != "max":
                                limit_bytes = int(limit_str)
                                docker_status["memory_limit"] = "{:.2f} GB".format(limit_bytes / 1024 / 1024 / 1024)
                                docker_status["memory_percent"] = "{:.2f}%".format(mem_bytes / limit_bytes * 100)
                elif os.path.exists("/sys/fs/cgroup/memory/memory.usage_in_bytes"):
                    with open("/sys/fs/cgroup/memory/memory.usage_in_bytes", "r") as f:
                        mem_bytes = int(f.read().strip())
                    docker_status["memory_usage"] = "{:.2f} MB".format(mem_bytes / 1024 / 1024)

                    with open("/sys/fs/cgroup/memory/memory.limit_in_bytes", "r") as f:
                        limit_bytes = int(f.read().strip())
                    if limit_bytes < 1e18:
                        docker_status["memory_limit"] = "{:.2f} GB".format(limit_bytes / 1024 / 1024 / 1024)
                        docker_status["memory_percent"] = "{:.2f}%".format(mem_bytes / limit_bytes * 100)
            except Exception as e:
                logger.debug(f"读取内存信息失败: {e}")

            try:
                if os.path.exists("/sys/fs/cgroup/cpu.stat"):
                    cpu_usage = 0
                    with open("/sys/fs/cgroup/cpu.stat", "r") as f:
                        for line in f:
                            if line.startswith("usage_usec"):
                                cpu_usage = int(line.split()[1])
                                break

                    time.sleep(0.1)
                    cpu_usage2 = 0
                    with open("/sys/fs/cgroup/cpu.stat", "r") as f:
                        for line in f:
                            if line.startswith("usage_usec"):
                                cpu_usage2 = int(line.split()[1])
                                break

                    cpu_percent = (cpu_usage2 - cpu_usage) / 0.1 / 1e6 * 100
                    docker_status["cpu_percent"] = "{:.2f}%".format(cpu_percent)
                elif os.path.exists("/sys/fs/cgroup/cpu/cpuacct.usage"):
                    with open("/sys/fs/cgroup/cpu/cpuacct.usage", "r") as f:
                        cpu_usage = int(f.read().strip())

                    time.sleep(0.1)
                    with open("/sys/fs/cgroup/cpu/cpuacct.usage", "r") as f:
                        cpu_usage2 = int(f.read().strip())

                    cpu_percent = (cpu_usage2 - cpu_usage) / 0.1 / 1e9 * 100
                    docker_status["cpu_percent"] = "{:.2f}%".format(cpu_percent)
            except Exception as e:
                logger.debug(f"读取CPU信息失败: {e}")

            try:
                result = subprocess.check_output(["uptime", "-p"]).decode("utf-8").strip()
                docker_status["uptime"] = result.replace("up ", "")
            except Exception as e:
                logger.debug(f"获取运行时间失败: {e}")

            docker_status["status"] = "Running"

        else:
            docker_status["status"] = "Not in Docker"
    except Exception as e:
        docker_status["status"] = f"Error: {str(e)}"

    return jsonify(docker_status)


# ==================== VIP 管理（管理员） ====================


@admin_api_bp.route("/vip/config", methods=["GET"])
@admin_required
def get_vip_config_api():
    """获取VIP配置"""
    config = database.get_vip_config()
    return jsonify(config)


@admin_api_bp.route("/vip/config", methods=["POST"])
@admin_required
def set_vip_config_api():
    """设置默认VIP天数"""
    data = request.json or {}
    days = data.get("default_vip_days", 0)

    if not isinstance(days, int) or days < 0:
        return jsonify({"error": "VIP天数必须是非负整数"}), 400

    database.set_default_vip_days(days)
    return jsonify({"message": "VIP配置已更新", "default_vip_days": days})


@admin_api_bp.route("/users/<int:user_id>/vip", methods=["POST"])
@admin_required
def set_user_vip_api(user_id):
    """设置用户VIP"""
    data = request.json or {}
    days = data.get("days", 30)

    valid_days = [7, 30, 365, 999999]
    if days not in valid_days:
        return jsonify({"error": "VIP天数必须是 7/30/365/999999 之一"}), 400

    if database.set_user_vip(user_id, days):
        vip_type = {7: "一周", 30: "一个月", 365: "一年", 999999: "永久"}[days]
        return jsonify({"message": f"VIP设置成功: {vip_type}"})
    return jsonify({"error": "设置失败,用户不存在"}), 400


@admin_api_bp.route("/users/<int:user_id>/vip", methods=["DELETE"])
@admin_required
def remove_user_vip_api(user_id):
    """移除用户VIP"""
    if database.remove_user_vip(user_id):
        return jsonify({"message": "VIP已移除"})
    return jsonify({"error": "移除失败"}), 400


@admin_api_bp.route("/users/<int:user_id>/vip", methods=["GET"])
@admin_required
def get_user_vip_info_api(user_id):
    """获取用户VIP信息(管理员)"""
    vip_info = database.get_user_vip_info(user_id)
    return jsonify(vip_info)


# ==================== 系统配置 / 定时 / 代理（管理员） ====================


@admin_api_bp.route("/system/config", methods=["GET"])
@admin_required
def get_system_config_api():
    """获取系统配置"""
    return jsonify(database.get_system_config())


@admin_api_bp.route("/system/config", methods=["POST"])
@admin_required
def update_system_config_api():
    """更新系统配置"""
    data = request.json or {}

    max_concurrent = data.get("max_concurrent_global")
    schedule_enabled = data.get("schedule_enabled")
    schedule_time = data.get("schedule_time")
    schedule_browse_type = data.get("schedule_browse_type")
    schedule_weekdays = data.get("schedule_weekdays")
    new_max_concurrent_per_account = data.get("max_concurrent_per_account")
    new_max_screenshot_concurrent = data.get("max_screenshot_concurrent")
    enable_screenshot = data.get("enable_screenshot")
    auto_approve_enabled = data.get("auto_approve_enabled")
    auto_approve_hourly_limit = data.get("auto_approve_hourly_limit")
    auto_approve_vip_days = data.get("auto_approve_vip_days")
    kdocs_enabled = data.get("kdocs_enabled")
    kdocs_doc_url = data.get("kdocs_doc_url")
    kdocs_default_unit = data.get("kdocs_default_unit")
    kdocs_sheet_name = data.get("kdocs_sheet_name")
    kdocs_sheet_index = data.get("kdocs_sheet_index")
    kdocs_unit_column = data.get("kdocs_unit_column")
    kdocs_image_column = data.get("kdocs_image_column")
    kdocs_admin_notify_enabled = data.get("kdocs_admin_notify_enabled")
    kdocs_admin_notify_email = data.get("kdocs_admin_notify_email")

    if max_concurrent is not None:
        if not isinstance(max_concurrent, int) or max_concurrent < 1:
            return jsonify({"error": "全局并发数必须大于0（建议：小型服务器2-5，中型5-10，大型10-20）"}), 400

    if new_max_concurrent_per_account is not None:
        if not isinstance(new_max_concurrent_per_account, int) or new_max_concurrent_per_account < 1:
            return jsonify({"error": "单账号并发数必须大于0（建议设为1，避免同一用户任务相互影响）"}), 400

    if new_max_screenshot_concurrent is not None:
        if not isinstance(new_max_screenshot_concurrent, int) or new_max_screenshot_concurrent < 1:
            return jsonify({"error": "截图并发数必须大于0（建议根据服务器配置设置，wkhtmltoimage 资源占用较低）"}), 400

    if enable_screenshot is not None:
        if isinstance(enable_screenshot, bool):
            enable_screenshot = 1 if enable_screenshot else 0
        if enable_screenshot not in (0, 1):
            return jsonify({"error": "截图开关必须是0或1"}), 400

    if schedule_time is not None:
        import re

        if not re.match(r"^([01]\\d|2[0-3]):([0-5]\\d)$", schedule_time):
            return jsonify({"error": "时间格式错误，应为 HH:MM"}), 400

    if schedule_browse_type is not None:
        normalized = validate_browse_type(schedule_browse_type, default=BROWSE_TYPE_SHOULD_READ)
        if not normalized:
            return jsonify({"error": "浏览类型无效"}), 400
        schedule_browse_type = normalized

    if schedule_weekdays is not None:
        try:
            days = [int(d.strip()) for d in schedule_weekdays.split(",") if d.strip()]
            if not all(1 <= d <= 7 for d in days):
                return jsonify({"error": "星期数字必须在1-7之间"}), 400
        except (ValueError, AttributeError):
            return jsonify({"error": "星期格式错误"}), 400

    if auto_approve_hourly_limit is not None:
        if not isinstance(auto_approve_hourly_limit, int) or auto_approve_hourly_limit < 1:
            return jsonify({"error": "每小时注册限制必须大于0"}), 400

    if auto_approve_vip_days is not None:
        if not isinstance(auto_approve_vip_days, int) or auto_approve_vip_days < 0:
            return jsonify({"error": "注册赠送VIP天数不能为负数"}), 400

    if kdocs_enabled is not None:
        if isinstance(kdocs_enabled, bool):
            kdocs_enabled = 1 if kdocs_enabled else 0
        if kdocs_enabled not in (0, 1):
            return jsonify({"error": "表格上传开关必须是0或1"}), 400

    if kdocs_doc_url is not None:
        kdocs_doc_url = str(kdocs_doc_url or "").strip()
        if kdocs_doc_url and not is_safe_outbound_url(kdocs_doc_url):
            return jsonify({"error": "文档链接格式不正确"}), 400

    if kdocs_default_unit is not None:
        kdocs_default_unit = str(kdocs_default_unit or "").strip()
        if len(kdocs_default_unit) > 50:
            return jsonify({"error": "默认县区长度不能超过50"}), 400

    if kdocs_sheet_name is not None:
        kdocs_sheet_name = str(kdocs_sheet_name or "").strip()
        if len(kdocs_sheet_name) > 50:
            return jsonify({"error": "Sheet名称长度不能超过50"}), 400

    if kdocs_sheet_index is not None:
        try:
            kdocs_sheet_index = int(kdocs_sheet_index)
        except Exception:
            return jsonify({"error": "Sheet序号必须是数字"}), 400
        if kdocs_sheet_index < 0:
            return jsonify({"error": "Sheet序号不能为负数"}), 400

    if kdocs_unit_column is not None:
        kdocs_unit_column = str(kdocs_unit_column or "").strip().upper()
        if not kdocs_unit_column:
            return jsonify({"error": "县区列不能为空"}), 400
        import re

        if not re.match(r"^[A-Z]{1,3}$", kdocs_unit_column):
            return jsonify({"error": "县区列格式错误"}), 400

    if kdocs_image_column is not None:
        kdocs_image_column = str(kdocs_image_column or "").strip().upper()
        if not kdocs_image_column:
            return jsonify({"error": "图片列不能为空"}), 400
        import re

        if not re.match(r"^[A-Z]{1,3}$", kdocs_image_column):
            return jsonify({"error": "图片列格式错误"}), 400

    if kdocs_admin_notify_enabled is not None:
        if isinstance(kdocs_admin_notify_enabled, bool):
            kdocs_admin_notify_enabled = 1 if kdocs_admin_notify_enabled else 0
        if kdocs_admin_notify_enabled not in (0, 1):
            return jsonify({"error": "管理员通知开关必须是0或1"}), 400

    if kdocs_admin_notify_email is not None:
        kdocs_admin_notify_email = str(kdocs_admin_notify_email or "").strip()
        if kdocs_admin_notify_email:
            is_valid, error_msg = validate_email(kdocs_admin_notify_email)
            if not is_valid:
                return jsonify({"error": error_msg}), 400

    old_config = database.get_system_config() or {}

    if not database.update_system_config(
        max_concurrent=max_concurrent,
        schedule_enabled=schedule_enabled,
        schedule_time=schedule_time,
        schedule_browse_type=schedule_browse_type,
        schedule_weekdays=schedule_weekdays,
        max_concurrent_per_account=new_max_concurrent_per_account,
        max_screenshot_concurrent=new_max_screenshot_concurrent,
        enable_screenshot=enable_screenshot,
        auto_approve_enabled=auto_approve_enabled,
        auto_approve_hourly_limit=auto_approve_hourly_limit,
        auto_approve_vip_days=auto_approve_vip_days,
        kdocs_enabled=kdocs_enabled,
        kdocs_doc_url=kdocs_doc_url,
        kdocs_default_unit=kdocs_default_unit,
        kdocs_sheet_name=kdocs_sheet_name,
        kdocs_sheet_index=kdocs_sheet_index,
        kdocs_unit_column=kdocs_unit_column,
        kdocs_image_column=kdocs_image_column,
        kdocs_admin_notify_enabled=kdocs_admin_notify_enabled,
        kdocs_admin_notify_email=kdocs_admin_notify_email,
    ):
        return jsonify({"error": "更新失败"}), 400

    try:
        new_config = database.get_system_config() or {}
        scheduler = get_task_scheduler()
        scheduler.update_limits(
            max_global=int(new_config.get("max_concurrent_global", old_config.get("max_concurrent_global", 2))),
            max_per_user=int(new_config.get("max_concurrent_per_account", old_config.get("max_concurrent_per_account", 1))),
        )
        if new_max_screenshot_concurrent is not None:
            try:
                from browser_pool_worker import resize_browser_worker_pool

                if resize_browser_worker_pool(int(new_config.get("max_screenshot_concurrent", new_max_screenshot_concurrent))):
                    logger.info(f"截图线程池并发已更新为: {new_config.get('max_screenshot_concurrent')}")
            except Exception as pool_error:
                logger.warning(f"截图线程池并发更新失败: {pool_error}")
    except Exception:
        pass

    if max_concurrent is not None and max_concurrent != old_config.get("max_concurrent_global"):
        logger.info(f"全局并发数已更新为: {max_concurrent}")
    if new_max_concurrent_per_account is not None and new_max_concurrent_per_account != old_config.get("max_concurrent_per_account"):
        logger.info(f"单用户并发数已更新为: {new_max_concurrent_per_account}")
    if new_max_screenshot_concurrent is not None:
        logger.info(f"截图并发数已更新为: {new_max_screenshot_concurrent}")

    return jsonify({"message": "系统配置已更新"})


@admin_api_bp.route("/kdocs/status", methods=["GET"])
@admin_required
def get_kdocs_status_api():
    """获取金山文档上传状态"""
    try:
        from services.kdocs_uploader import get_kdocs_uploader

        uploader = get_kdocs_uploader()
        status = uploader.get_status()
        live = str(request.args.get("live", "")).lower() in ("1", "true", "yes")
        if live:
            live_status = uploader.refresh_login_status()
            if live_status.get("success"):
                logged_in = bool(live_status.get("logged_in"))
                status["logged_in"] = logged_in
                status["last_login_ok"] = logged_in
                status["login_required"] = not logged_in
            if live_status.get("error"):
                status["last_error"] = live_status.get("error")
        else:
            status["logged_in"] = True if status.get("last_login_ok") else False if status.get("last_login_ok") is False else None
            if status.get("last_login_ok") is True and status.get("last_error") == "操作超时":
                status["last_error"] = None
        return jsonify(status)
    except Exception as e:
        return jsonify({"error": f"获取状态失败: {e}"}), 500


@admin_api_bp.route("/kdocs/qr", methods=["POST"])
@admin_required
def get_kdocs_qr_api():
    """获取金山文档登录二维码"""
    try:
        from services.kdocs_uploader import get_kdocs_uploader

        uploader = get_kdocs_uploader()
        data = request.get_json(silent=True) or {}
        force = bool(data.get("force"))
        if not force:
            force = str(request.args.get("force", "")).lower() in ("1", "true", "yes")
        result = uploader.request_qr(force=force)
        if not result.get("success"):
            return jsonify({"error": result.get("error", "获取二维码失败")}), 400
        return jsonify(result)
    except Exception as e:
        return jsonify({"error": f"获取二维码失败: {e}"}), 500


@admin_api_bp.route("/kdocs/clear-login", methods=["POST"])
@admin_required
def clear_kdocs_login_api():
    """清除金山文档登录态"""
    try:
        from services.kdocs_uploader import get_kdocs_uploader

        uploader = get_kdocs_uploader()
        result = uploader.clear_login()
        if not result.get("success"):
            return jsonify({"error": result.get("error", "清除失败")}), 400
        return jsonify({"success": True})
    except Exception as e:
        return jsonify({"error": f"清除失败: {e}"}), 500


@admin_api_bp.route("/schedule/execute", methods=["POST"])
@admin_required
def execute_schedule_now():
    """立即执行定时任务（无视定时时间和星期限制）"""
    try:
        threading.Thread(target=run_scheduled_task, args=(True,), daemon=True).start()
        logger.info("[立即执行定时任务] 管理员手动触发定时任务执行（跳过星期检查）")
        return jsonify({"message": "定时任务已开始执行，请查看任务列表获取进度"})
    except Exception as e:
        logger.error(f"[立即执行定时任务] 启动失败: {str(e)}")
        return jsonify({"error": f"启动失败: {str(e)}"}), 500


@admin_api_bp.route("/proxy/config", methods=["GET"])
@admin_required
def get_proxy_config_api():
    """获取代理配置"""
    config_data = database.get_system_config()
    return jsonify(
        {
            "proxy_enabled": config_data.get("proxy_enabled", 0),
            "proxy_api_url": config_data.get("proxy_api_url", ""),
            "proxy_expire_minutes": config_data.get("proxy_expire_minutes", 3),
        }
    )


@admin_api_bp.route("/proxy/config", methods=["POST"])
@admin_required
def update_proxy_config_api():
    """更新代理配置"""
    data = request.json or {}
    proxy_enabled = data.get("proxy_enabled")
    proxy_api_url = (data.get("proxy_api_url", "") or "").strip()
    proxy_expire_minutes = data.get("proxy_expire_minutes")

    if proxy_enabled is not None and proxy_enabled not in [0, 1]:
        return jsonify({"error": "proxy_enabled必须是0或1"}), 400

    if proxy_expire_minutes is not None:
        if not isinstance(proxy_expire_minutes, int) or proxy_expire_minutes < 1:
            return jsonify({"error": "代理有效期必须是大于0的整数"}), 400

    if database.update_system_config(
        proxy_enabled=proxy_enabled,
        proxy_api_url=proxy_api_url,
        proxy_expire_minutes=proxy_expire_minutes,
    ):
        return jsonify({"message": "代理配置已更新"})
    return jsonify({"error": "更新失败"}), 400


@admin_api_bp.route("/proxy/test", methods=["POST"])
@admin_required
def test_proxy_api():
    """测试代理连接"""
    data = request.json or {}
    api_url = (data.get("api_url") or "").strip()

    if not api_url:
        return jsonify({"error": "请提供API地址"}), 400

    if not is_safe_outbound_url(api_url):
        return jsonify({"error": "API地址不可用或不安全"}), 400

    try:
        response = requests.get(api_url, timeout=10)
        if response.status_code == 200:
            ip_port = response.text.strip()
            if ip_port and ":" in ip_port:
                return jsonify({"success": True, "proxy": ip_port, "message": f"代理获取成功: {ip_port}"})
            return jsonify({"success": False, "message": f"代理格式错误: {ip_port}"}), 400
        return jsonify({"success": False, "message": f"HTTP错误: {response.status_code}"}), 400
    except Exception as e:
        return jsonify({"success": False, "message": f"连接失败: {str(e)}"}), 500


@admin_api_bp.route("/server/info", methods=["GET"])
@admin_required
def get_server_info_api():
    """获取服务器信息"""
    import psutil

    cpu_percent = _get_server_cpu_percent()

    memory = psutil.virtual_memory()
    memory_total = f"{memory.total / (1024**3):.1f}GB"
    memory_used = f"{memory.used / (1024**3):.1f}GB"
    memory_percent = memory.percent

    disk = psutil.disk_usage("/")
    disk_total = f"{disk.total / (1024**3):.1f}GB"
    disk_used = f"{disk.used / (1024**3):.1f}GB"
    disk_percent = disk.percent

    boot_time = datetime.fromtimestamp(psutil.boot_time(), tz=BEIJING_TZ)
    uptime_delta = get_beijing_now() - boot_time
    days = uptime_delta.days
    hours = uptime_delta.seconds // 3600
    uptime = f"{days}天{hours}小时"

    return jsonify(
        {
            "cpu_percent": cpu_percent,
            "memory_total": memory_total,
            "memory_used": memory_used,
            "memory_percent": memory_percent,
            "disk_total": disk_total,
            "disk_used": disk_used,
            "disk_percent": disk_percent,
            "uptime": uptime,
        }
    )


# ==================== 任务统计与日志（管理员） ====================


@admin_api_bp.route("/task/stats", methods=["GET"])
@admin_required
def get_task_stats_api():
    """获取任务统计数据"""
    date_filter = request.args.get("date")
    stats = database.get_task_stats(date_filter)
    return jsonify(stats)


@admin_api_bp.route("/task/running", methods=["GET"])
@admin_required
def get_running_tasks_api():
    """获取当前运行中和排队中的任务"""
    import time as time_mod

    current_time = time_mod.time()
    running = []
    queuing = []

    for account_id, info in safe_iter_task_status_items():
        elapsed = int(current_time - info.get("start_time", current_time))

        user = database.get_user_by_id(info.get("user_id"))
        user_username = user["username"] if user else "N/A"

        progress = info.get("progress", {"items": 0, "attachments": 0})
        task_info = {
            "account_id": account_id,
            "user_id": info.get("user_id"),
            "user_username": user_username,
            "username": info.get("username"),
            "browse_type": info.get("browse_type"),
            "source": info.get("source", "manual"),
            "detail_status": info.get("detail_status", "未知"),
            "progress_items": progress.get("items", 0),
            "progress_attachments": progress.get("attachments", 0),
            "elapsed_seconds": elapsed,
            "elapsed_display": f"{elapsed // 60}分{elapsed % 60}秒" if elapsed >= 60 else f"{elapsed}秒",
        }

        if info.get("status") == "运行中":
            running.append(task_info)
        else:
            queuing.append(task_info)

    running.sort(key=lambda x: x["elapsed_seconds"], reverse=True)
    queuing.sort(key=lambda x: x["elapsed_seconds"], reverse=True)

    try:
        max_concurrent = int(get_task_scheduler().max_global)
    except Exception:
        max_concurrent = int((database.get_system_config() or {}).get("max_concurrent_global", 2))

    return jsonify(
        {
            "running": running,
            "queuing": queuing,
            "running_count": len(running),
            "queuing_count": len(queuing),
            "max_concurrent": max_concurrent,
        }
    )


@admin_api_bp.route("/task/logs", methods=["GET"])
@admin_required
def get_task_logs_api():
    """获取任务日志列表（支持分页和多种筛选）"""
    limit = int(request.args.get("limit", 20))
    offset = int(request.args.get("offset", 0))
    date_filter = request.args.get("date")
    status_filter = request.args.get("status")
    source_filter = request.args.get("source")
    user_id_filter = request.args.get("user_id")
    account_filter = request.args.get("account")

    if user_id_filter:
        try:
            user_id_filter = int(user_id_filter)
        except ValueError:
            user_id_filter = None

    result = database.get_task_logs(
        limit=limit,
        offset=offset,
        date_filter=date_filter,
        status_filter=status_filter,
        source_filter=source_filter,
        user_id_filter=user_id_filter,
        account_filter=account_filter,
    )
    return jsonify(result)


@admin_api_bp.route("/task/logs/clear", methods=["POST"])
@admin_required
def clear_old_task_logs_api():
    """清理旧的任务日志"""
    data = request.json or {}
    days = data.get("days", 30)

    if not isinstance(days, int) or days < 1:
        return jsonify({"error": "天数必须是大于0的整数"}), 400

    deleted_count = database.delete_old_task_logs(days)
    return jsonify({"message": f"已删除{days}天前的{deleted_count}条日志"})


@admin_api_bp.route("/docker/restart", methods=["POST"])
@admin_required
def restart_docker_container():
    """重启Docker容器"""
    import subprocess

    try:
        reauth_response = _require_admin_reauth()
        if reauth_response:
            return reauth_response
        if not os.path.exists("/.dockerenv"):
            return jsonify({"error": "当前不在Docker容器中运行"}), 400

        logger.info("[系统] 管理员触发Docker容器重启")

        restart_script = """
import os
import time

time.sleep(3)
os._exit(0)
"""

        with open("/tmp/restart_container.py", "w") as f:
            f.write(restart_script)

        subprocess.Popen(
            ["python", "/tmp/restart_container.py"],
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL,
            start_new_session=True,
        )

        return jsonify({"success": True, "message": "容器将在3秒后重启，请稍后刷新页面"})
    except Exception as e:
        logger.error(f"[系统] Docker容器重启失败: {str(e)}")
        return jsonify({"error": f"重启失败: {str(e)}"}), 500


# ==================== 密码重置 / 反馈（管理员） ====================


@admin_api_bp.route("/admin/password", methods=["PUT"])
@admin_required
def update_admin_password():
    """修改管理员密码"""
    data = request.json or {}
    new_password = (data.get("new_password") or "").strip()

    if not new_password:
        return jsonify({"error": "密码不能为空"}), 400

    username = session.get("admin_username")
    if database.update_admin_password(username, new_password):
        return jsonify({"success": True})
    return jsonify({"error": "修改失败"}), 400


@admin_api_bp.route("/admin/username", methods=["PUT"])
@admin_required
def update_admin_username():
    """修改管理员用户名"""
    data = request.json or {}
    new_username = (data.get("new_username") or "").strip()

    if not new_username:
        return jsonify({"error": "用户名不能为空"}), 400

    old_username = session.get("admin_username")
    if database.update_admin_username(old_username, new_username):
        session["admin_username"] = new_username
        return jsonify({"success": True})
    return jsonify({"error": "修改失败，用户名可能已存在"}), 400


@admin_api_bp.route("/users/<int:user_id>/reset_password", methods=["POST"])
@admin_required
def admin_reset_password_route(user_id):
    """管理员直接重置用户密码（无需审核）"""
    data = request.json or {}
    new_password = (data.get("new_password") or "").strip()

    if not new_password:
        return jsonify({"error": "新密码不能为空"}), 400

    is_valid, error_msg = validate_password(new_password)
    if not is_valid:
        return jsonify({"error": error_msg}), 400

    if database.admin_reset_user_password(user_id, new_password):
        return jsonify({"message": "密码重置成功"})
    return jsonify({"error": "重置失败，用户不存在"}), 400


@admin_api_bp.route("/feedbacks", methods=["GET"])
@admin_required
def get_all_feedbacks():
    """管理员获取所有反馈"""
    status = request.args.get("status")
    try:
        limit = int(request.args.get("limit", 100))
        offset = int(request.args.get("offset", 0))
        limit = min(max(1, limit), 1000)
        offset = max(0, offset)
    except (ValueError, TypeError):
        return jsonify({"error": "无效的分页参数"}), 400

    feedbacks = database.get_bug_feedbacks(limit=limit, offset=offset, status_filter=status)
    stats = database.get_feedback_stats()
    return jsonify({"feedbacks": feedbacks, "stats": stats})


@admin_api_bp.route("/feedbacks/<int:feedback_id>/reply", methods=["POST"])
@admin_required
def reply_to_feedback(feedback_id):
    """管理员回复反馈"""
    data = request.get_json() or {}
    reply = (data.get("reply") or "").strip()

    if not reply:
        return jsonify({"error": "回复内容不能为空"}), 400

    if database.reply_feedback(feedback_id, reply):
        return jsonify({"message": "回复成功"})
    return jsonify({"error": "反馈不存在"}), 404


@admin_api_bp.route("/feedbacks/<int:feedback_id>/close", methods=["POST"])
@admin_required
def close_feedback_api(feedback_id):
    """管理员关闭反馈"""
    if database.close_feedback(feedback_id):
        return jsonify({"message": "已关闭"})
    return jsonify({"error": "反馈不存在"}), 404


@admin_api_bp.route("/feedbacks/<int:feedback_id>", methods=["DELETE"])
@admin_required
def delete_feedback_api(feedback_id):
    """管理员删除反馈"""
    if database.delete_feedback(feedback_id):
        return jsonify({"message": "已删除"})
    return jsonify({"error": "反馈不存在"}), 404


# ==================== 断点续传（管理员） ====================


@admin_api_bp.route("/checkpoint/paused")
@admin_required
def checkpoint_get_paused():
    try:
        user_id = request.args.get("user_id", type=int)
        tasks = get_checkpoint_mgr().get_paused_tasks(user_id=user_id)
        return jsonify({"success": True, "tasks": tasks})
    except Exception as e:
        logger.error(f"获取暂停任务失败: {e}")
        return jsonify({"success": False, "message": str(e)}), 500


@admin_api_bp.route("/checkpoint/<task_id>/resume", methods=["POST"])
@admin_required
def checkpoint_resume(task_id):
    try:
        checkpoint_mgr = get_checkpoint_mgr()
        checkpoint = checkpoint_mgr.get_checkpoint(task_id)
        if not checkpoint:
            return jsonify({"success": False, "message": "任务不存在"}), 404
        if checkpoint["status"] != "paused":
            return jsonify({"success": False, "message": "任务未暂停"}), 400
        if checkpoint_mgr.resume_task(task_id):
            user_id = checkpoint["user_id"]
            if not safe_get_user_accounts_snapshot(user_id):
                load_user_accounts(user_id)
            ok, msg = submit_account_task(
                user_id=user_id,
                account_id=checkpoint["account_id"],
                browse_type=checkpoint["browse_type"],
                enable_screenshot=True,
                source="resumed",
            )
            if not ok:
                return jsonify({"success": False, "message": msg}), 400
            return jsonify({"success": True})
        return jsonify({"success": False}), 500
    except Exception as e:
        logger.error(f"恢复任务失败: {e}")
        return jsonify({"success": False, "message": str(e)}), 500


@admin_api_bp.route("/checkpoint/<task_id>/abandon", methods=["POST"])
@admin_required
def checkpoint_abandon(task_id):
    try:
        if get_checkpoint_mgr().abandon_task(task_id):
            return jsonify({"success": True})
        return jsonify({"success": False}), 404
    except Exception as e:
        return jsonify({"success": False, "message": str(e)}), 500


# ==================== 邮件服务（管理员） ====================


@admin_api_bp.route("/email/settings", methods=["GET"])
@admin_required
def get_email_settings_api():
    """获取全局邮件设置"""
    try:
        settings = email_service.get_email_settings()
        return jsonify(settings)
    except Exception as e:
        logger.error(f"获取邮件设置失败: {e}")
        return jsonify({"error": str(e)}), 500


@admin_api_bp.route("/email/settings", methods=["POST"])
@admin_required
def update_email_settings_api():
    """更新全局邮件设置"""
    try:
        data = request.json or {}
        enabled = data.get("enabled", False)
        failover_enabled = data.get("failover_enabled", True)
        register_verify_enabled = data.get("register_verify_enabled")
        login_alert_enabled = data.get("login_alert_enabled")
        base_url = data.get("base_url")
        task_notify_enabled = data.get("task_notify_enabled")

        email_service.update_email_settings(
            enabled=enabled,
            failover_enabled=failover_enabled,
            register_verify_enabled=register_verify_enabled,
            login_alert_enabled=login_alert_enabled,
            base_url=base_url,
            task_notify_enabled=task_notify_enabled,
        )
        return jsonify({"success": True})
    except Exception as e:
        logger.error(f"更新邮件设置失败: {e}")
        return jsonify({"error": str(e)}), 500


@admin_api_bp.route("/smtp/configs", methods=["GET"])
@admin_required
def get_smtp_configs_api():
    """获取所有SMTP配置列表"""
    try:
        configs = email_service.get_smtp_configs(include_password=False)
        return jsonify(configs)
    except Exception as e:
        logger.error(f"获取SMTP配置失败: {e}")
        return jsonify({"error": str(e)}), 500


@admin_api_bp.route("/smtp/configs", methods=["POST"])
@admin_required
def create_smtp_config_api():
    """创建SMTP配置"""
    try:
        data = request.json or {}
        if not data.get("host"):
            return jsonify({"error": "SMTP服务器地址不能为空"}), 400
        if not data.get("username"):
            return jsonify({"error": "SMTP用户名不能为空"}), 400

        config_id = email_service.create_smtp_config(data)
        return jsonify({"success": True, "id": config_id})
    except Exception as e:
        logger.error(f"创建SMTP配置失败: {e}")
        return jsonify({"error": str(e)}), 500


@admin_api_bp.route("/smtp/configs/<int:config_id>", methods=["GET"])
@admin_required
def get_smtp_config_api(config_id):
    """获取单个SMTP配置详情"""
    try:
        config_data = email_service.get_smtp_config(config_id, include_password=False)
        if not config_data:
            return jsonify({"error": "配置不存在"}), 404
        return jsonify(config_data)
    except Exception as e:
        logger.error(f"获取SMTP配置失败: {e}")
        return jsonify({"error": str(e)}), 500


@admin_api_bp.route("/smtp/configs/<int:config_id>", methods=["PUT"])
@admin_required
def update_smtp_config_api(config_id):
    """更新SMTP配置"""
    try:
        data = request.json or {}
        if email_service.update_smtp_config(config_id, data):
            return jsonify({"success": True})
        return jsonify({"error": "更新失败"}), 400
    except Exception as e:
        logger.error(f"更新SMTP配置失败: {e}")
        return jsonify({"error": str(e)}), 500


@admin_api_bp.route("/smtp/configs/<int:config_id>", methods=["DELETE"])
@admin_required
def delete_smtp_config_api(config_id):
    """删除SMTP配置"""
    try:
        if email_service.delete_smtp_config(config_id):
            return jsonify({"success": True})
        return jsonify({"error": "删除失败"}), 400
    except Exception as e:
        logger.error(f"删除SMTP配置失败: {e}")
        return jsonify({"error": str(e)}), 500


@admin_api_bp.route("/smtp/configs/<int:config_id>/test", methods=["POST"])
@admin_required
def test_smtp_config_api(config_id):
    """测试SMTP配置"""
    try:
        data = request.json or {}
        test_email = str(data.get("email", "") or "").strip()
        if not test_email:
            return jsonify({"error": "请提供测试邮箱"}), 400

        is_valid, error_msg = validate_email(test_email)
        if not is_valid:
            return jsonify({"error": error_msg}), 400

        result = email_service.test_smtp_config(config_id, test_email)
        return jsonify(result)
    except Exception as e:
        logger.error(f"测试SMTP配置失败: {e}")
        return jsonify({"success": False, "error": str(e)}), 500


@admin_api_bp.route("/smtp/configs/<int:config_id>/primary", methods=["POST"])
@admin_required
def set_primary_smtp_config_api(config_id):
    """设置主SMTP配置"""
    try:
        if email_service.set_primary_smtp_config(config_id):
            return jsonify({"success": True})
        return jsonify({"error": "设置失败"}), 400
    except Exception as e:
        logger.error(f"设置主SMTP配置失败: {e}")
        return jsonify({"error": str(e)}), 500


@admin_api_bp.route("/smtp/configs/primary/clear", methods=["POST"])
@admin_required
def clear_primary_smtp_config_api():
    """取消主SMTP配置"""
    try:
        email_service.clear_primary_smtp_config()
        return jsonify({"success": True})
    except Exception as e:
        logger.error(f"取消主SMTP配置失败: {e}")
        return jsonify({"error": str(e)}), 500


@admin_api_bp.route("/email/stats", methods=["GET"])
@admin_required
def get_email_stats_api():
    """获取邮件发送统计"""
    try:
        stats = email_service.get_email_stats()
        return jsonify(stats)
    except Exception as e:
        logger.error(f"获取邮件统计失败: {e}")
        return jsonify({"error": str(e)}), 500


@admin_api_bp.route("/email/logs", methods=["GET"])
@admin_required
def get_email_logs_api():
    """获取邮件发送日志"""
    try:
        page = request.args.get("page", 1, type=int)
        page_size = request.args.get("page_size", 20, type=int)
        email_type = request.args.get("type", None)
        status = request.args.get("status", None)

        page_size = min(max(page_size, 10), 100)
        result = email_service.get_email_logs(page, page_size, email_type, status)
        return jsonify(result)
    except Exception as e:
        logger.error(f"获取邮件日志失败: {e}")
        return jsonify({"error": str(e)}), 500


@admin_api_bp.route("/email/logs/cleanup", methods=["POST"])
@admin_required
def cleanup_email_logs_api():
    """清理过期邮件日志"""
    try:
        data = request.json or {}
        days = data.get("days", 30)
        days = min(max(days, 7), 365)

        deleted = email_service.cleanup_email_logs(days)
        return jsonify({"success": True, "deleted": deleted})
    except Exception as e:
        logger.error(f"清理邮件日志失败: {e}")
        return jsonify({"error": str(e)}), 500
