# -*- coding: utf-8 -*-
"""
service_manage.py — 服务管理模块路由

API 路由：/api/service-manage/*

页面路由（/service-manage/*）已迁移到 Vue SPA，由 server.py 的 SPA 回退处理。

权限：
  - 所有操作仅管理员可执行（_require_admin_json）
  - 普通用户无权限访问
"""

from __future__ import annotations

import logging
from datetime import datetime

from flask import (
    Blueprint, session, request, jsonify,
    send_file,
)

from utils.audit import log_audit
from services.service_manage import (
    list_targets, get_target_view, create_target,
    update_target, delete_target, test_connection,
    save_project, get_project,
    get_activation_file, get_deploy_view_file, get_self_check_file,
    save_uploaded_file, upload_license_file, check_license_task,
    _upload_to_target,
)

logger = logging.getLogger("service_manage.routes")

bp = Blueprint('service-manage', __name__)


# ============================================================
# 内部辅助：权限
# ============================================================

def _current_user():
    return session.get('user', {})


def _role():
    return _current_user().get('role', '')


def _require_admin_json():
    """管理员权限校验，返回 (response, status) 或 None。"""
    if 'user' not in session:
        return jsonify({"success": False, "error": {"code": 401, "message": "未登录"}}), 401
    if _role() != 'admin':
        return jsonify({"success": False, "error": {"code": 403, "message": "权限不足，仅管理员可操作"}}), 403
    return None


def _current_user_name() -> str:
    return _current_user().get('username', 'unknown')


def _audit(action: str, target: str) -> None:
    """记录审计日志（统一 dict 格式，与 utils/audit.log_audit 一致）。"""
    log_audit({
        'timestamp': datetime.now().isoformat(),
        'user': _current_user_name(),
        'module': 'service_manage',
        'action': action,
        'target': target,
        'ip': request.remote_addr or '',
    })


# ============================================================
# API：目标配置（管理员）
# ============================================================

@bp.route('/api/service-manage/targets', methods=['GET'])
def manage_targets_list():
    """目标列表。"""
    guard = _require_admin_json()
    if guard:
        return guard
    try:
        targets = list_targets()
        return jsonify({"success": True, "targets": targets})
    except Exception as e:
        logger.exception("获取目标列表失败")
        return jsonify({"success": False, "error": {"code": 500, "message": str(e)}}), 500


@bp.route('/api/service-manage/targets', methods=['POST'])
def manage_targets_create():
    """新增目标。"""
    guard = _require_admin_json()
    if guard:
        return guard
    try:
        data = request.get_json(force=True) or {}
        target = create_target(data)
        _audit("create_target", f"新增目标: {target['name']} ({target['host']})")
        return jsonify({"success": True, "target": target})
    except ValueError as e:
        return jsonify({"success": False, "error": {"code": 400, "message": str(e)}}), 400
    except Exception as e:
        logger.exception("新增目标失败")
        return jsonify({"success": False, "error": {"code": 500, "message": str(e)}}), 500


@bp.route('/api/service-manage/targets/<target_id>', methods=['PUT'])
def manage_targets_update(target_id: str):
    """更新目标。"""
    guard = _require_admin_json()
    if guard:
        return guard
    try:
        data = request.get_json(force=True) or {}
        target = update_target(target_id, data)
        _audit("update_target", f"更新目标: {target_id}")
        return jsonify({"success": True, "target": target})
    except ValueError as e:
        return jsonify({"success": False, "error": {"code": 400, "message": str(e)}}), 400
    except Exception as e:
        logger.exception("更新目标失败")
        return jsonify({"success": False, "error": {"code": 500, "message": str(e)}}), 500


@bp.route('/api/service-manage/targets/<target_id>', methods=['DELETE'])
def manage_targets_delete(target_id: str):
    """删除目标。"""
    guard = _require_admin_json()
    if guard:
        return guard
    try:
        ok = delete_target(target_id)
        if ok:
            _audit("delete_target", f"删除目标: {target_id}")
            return jsonify({"success": True})
        return jsonify({"success": False, "error": {"code": 404, "message": "目标不存在"}}), 404
    except Exception as e:
        logger.exception("删除目标失败")
        return jsonify({"success": False, "error": {"code": 500, "message": str(e)}}), 500


@bp.route('/api/service-manage/targets/test', methods=['POST'])
def manage_targets_test():
    """测试连接。"""
    guard = _require_admin_json()
    if guard:
        return guard
    try:
        data = request.get_json(force=True) or {}
        result = test_connection(data)
        return jsonify(result)
    except Exception as e:
        logger.exception("测试连接异常")
        return jsonify({"success": False, "message": f"测试连接异常: {e}"}), 500


# ============================================================
# API：服务授权（管理员）
# ============================================================

@bp.route('/api/service-manage/license/download', methods=['GET'])
def license_download():
    """下载激活文件（阶段一：生成占位文件，后续对接目标服务器代理）。"""
    guard = _require_admin_json()
    if guard:
        return guard
    try:
        filepath = get_activation_file()
        _audit("download_license", "下载激活文件")
        return send_file(
            filepath,
            as_attachment=True,
            download_name="licences.zip",
            mimetype="application/zip",
        )
    except Exception as e:
        logger.exception("下载激活文件失败")
        return jsonify({"success": False, "error": {"code": 500, "message": str(e)}}), 500


@bp.route('/api/service-manage/license/upload', methods=['POST'])
def license_upload():
    """上传授权文件（代理到目标服务器；无目标时本地暂存）。"""
    guard = _require_admin_json()
    if guard:
        return guard
    try:
        if 'file' not in request.files:
            return jsonify({"success": False, "error": {"code": 400, "message": "未选择文件"}}), 400
        file_storage = request.files['file']
        if not file_storage.filename:
            return jsonify({"success": False, "error": {"code": 400, "message": "文件名为空"}}), 400

        result = upload_license_file(file_storage)
        _audit("upload_license", f"上传授权文件: {result.get('filename', '')}")
        return jsonify(result)
    except Exception as e:
        logger.exception("上传授权文件失败")
        return jsonify({"success": False, "error": {"code": 500, "message": str(e)}}), 500


@bp.route('/api/service-manage/license/task-status', methods=['GET'])
def license_task_status():
    """查询授权任务状态（代理到目标服务器；本地模式直接返回完成）。"""
    guard = _require_admin_json()
    if guard:
        return guard
    try:
        task_id = request.args.get("taskId", "")
        result = check_license_task(task_id)
        result["taskId"] = task_id
        return jsonify(result)
    except Exception as e:
        logger.exception("查询授权任务状态失败")
        return jsonify({"success": False, "error": {"code": 500, "message": str(e)}}), 500


@bp.route('/api/service-manage/project', methods=['GET', 'POST'])
def manage_project():
    """项目信息：GET 读取 / POST 保存。"""
    guard = _require_admin_json()
    if guard:
        return guard

    if request.method == 'GET':
        try:
            project = get_project()
            return jsonify({"success": True, "project": project})
        except Exception as e:
            logger.exception("读取项目信息失败")
            return jsonify({"success": False, "error": {"code": 500, "message": str(e)}}), 500

    # POST 保存
    try:
        data = request.get_json(force=True) or {}
        result = save_project(data)
        _audit("save_project", "保存项目信息")
        return jsonify(result)
    except Exception as e:
        logger.exception("保存项目信息失败")
        return jsonify({"success": False, "error": {"code": 500, "message": str(e)}}), 500


@bp.route('/api/service-manage/deploy-view/download', methods=['GET'])
def deploy_view_download():
    """下载部署视图（阶段一：生成占位文件）。"""
    guard = _require_admin_json()
    if guard:
        return guard
    try:
        filepath = get_deploy_view_file()
        _audit("download_deploy_view", "下载部署视图")
        return send_file(
            filepath,
            as_attachment=True,
            download_name=filepath.name,
            mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        )
    except Exception as e:
        logger.exception("下载部署视图失败")
        return jsonify({"success": False, "error": {"code": 500, "message": str(e)}}), 500


@bp.route('/api/service-manage/deploy-view/upload', methods=['POST'])
def deploy_view_upload():
    """上传部署视图（代理到目标服务器；无目标时本地暂存）。"""
    guard = _require_admin_json()
    if guard:
        return guard
    try:
        if 'file' not in request.files:
            return jsonify({"success": False, "error": {"code": 400, "message": "未选择文件"}}), 400
        file_storage = request.files['file']
        if not file_storage.filename:
            return jsonify({"success": False, "error": {"code": 400, "message": "文件名为空"}}), 400

        result = _upload_to_target(file_storage, "upload_deploy_view")
        _audit("upload_deploy_view", f"上传部署视图: {file_storage.filename}")
        return jsonify(result)
    except Exception as e:
        logger.exception("上传部署视图失败")
        return jsonify({"success": False, "error": {"code": 500, "message": str(e)}}), 500


@bp.route('/api/service-manage/self-check/download', methods=['GET'])
def self_check_download():
    """下载自检文档（阶段一：生成占位文件）。"""
    guard = _require_admin_json()
    if guard:
        return guard
    try:
        filepath = get_self_check_file()
        _audit("download_self_check", "下载自检文档")
        return send_file(
            filepath,
            as_attachment=True,
            download_name=filepath.name,
            mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        )
    except Exception as e:
        logger.exception("下载自检文档失败")
        return jsonify({"success": False, "error": {"code": 500, "message": str(e)}}), 500


@bp.route('/api/service-manage/self-check/upload', methods=['POST'])
def self_check_upload():
    """上传自检文档（代理到目标服务器；无目标时本地暂存）。"""
    guard = _require_admin_json()
    if guard:
        return guard
    try:
        if 'file' not in request.files:
            return jsonify({"success": False, "error": {"code": 400, "message": "未选择文件"}}), 400
        file_storage = request.files['file']
        if not file_storage.filename:
            return jsonify({"success": False, "error": {"code": 400, "message": "文件名为空"}}), 400

        result = _upload_to_target(file_storage, "upload_self_check")
        _audit("upload_self_check", f"上传自检文档: {file_storage.filename}")
        return jsonify(result)
    except Exception as e:
        logger.exception("上传自检文档失败")
        return jsonify({"success": False, "error": {"code": 500, "message": str(e)}}), 500