# -*- coding: utf-8 -*-
"""test_routes_sm.py — 服务监测路由集成测试（test client）

验证：角色控制、目标 CRUD API、报告查看、SSE 流。
"""

import json

import pytest

from service_monitor.services import target_service, report_service


@pytest.fixture
def app(tmp_data, monkeypatch):
    import os
    monkeypatch.setenv("SECRET_KEY", "test-secret")
    import server
    application = server.create_app()
    application.config["TESTING"] = True
    return application


@pytest.fixture
def client(app):
    return app.test_client()


@pytest.fixture
def admin(app):
    """独立的管理员 client（不与 user 共享 session）。"""
    c = app.test_client()
    with c.session_transaction() as s:
        s["user"] = {"id": 1, "username": "admin", "role": "admin"}
    return c


@pytest.fixture
def user(app):
    """独立的普通用户 client。"""
    c = app.test_client()
    with c.session_transaction() as s:
        s["user"] = {"id": 2, "username": "u1", "role": "user"}
    return c


# ============================================================
# 页面访问
# ============================================================

class TestPages:

    def test_index_requires_login(self, client):
        r = client.get("/service-monitor")
        assert r.status_code == 302   # 重定向登录

    def test_admin_can_access(self, admin):
        r = admin.get("/service-monitor")
        assert r.status_code == 200
        assert "监测目标".encode() in r.data

    def test_user_redirected_from_targets(self, user):
        r = user.get("/service-monitor/targets")
        assert r.status_code == 302

    def test_admin_can_access_targets(self, admin):
        r = admin.get("/service-monitor/targets")
        assert r.status_code == 200

    def test_run_page_admin_only(self, user, admin, tmp_data):
        tid = target_service.get_target("local")["id"]
        assert user.get(f"/service-monitor/run/{tid}").status_code == 302
        assert admin.get(f"/service-monitor/run/{tid}?suite=quick").status_code == 200


# ============================================================
# 目标 API 权限
# ============================================================

class TestTargetAPI:

    def test_create_requires_admin(self, user):
        r = user.post("/api/service-monitor/targets", json={
            "name": "x", "host": "1.2.3.4", "username": "root", "password": "p"})
        assert r.status_code == 403

    def test_create_admin_ok(self, admin):
        r = admin.post("/api/service-monitor/targets", json={
            "name": "DB", "host": "192.168.1.10", "port": 22,
            "username": "root", "password": "pw"})
        assert r.status_code == 200
        data = r.get_json()
        assert data["success"] is True
        # 不回传密码明文/密文，只有布尔标记 has_password
        target = data["target"]
        assert target["has_password"] is True
        assert "password_enc" not in target
        assert "password" not in target or target.get("password") in (None, "")
        # 明文密码值不应出现在响应里
        assert "pw" not in json.dumps(target)

    def test_create_bad_input(self, admin):
        r = admin.post("/api/service-monitor/targets", json={"name": "", "host": "1.2.3.4"})
        assert r.status_code == 400

    def test_list_targets_user_local_only(self, user, admin):
        admin.post("/api/service-monitor/targets", json={
            "name": "远程", "host": "1.2.3.4", "username": "root", "password": "p"})
        u_list = user.get("/api/service-monitor/targets").get_json()["targets"]
        a_list = admin.get("/api/service-monitor/targets").get_json()["targets"]
        assert len(u_list) == 1 and u_list[0]["type"] == "local"
        assert len(a_list) == 2

    def test_delete_admin_ok(self, admin):
        tid = admin.post("/api/service-monitor/targets", json={
            "name": "x", "host": "1.2.3.4", "username": "root", "password": "p"}).get_json()["target"]["id"]
        assert admin.delete(f"/api/service-monitor/targets/{tid}").status_code == 200
        assert admin.delete("/api/service-monitor/targets/local").status_code == 400  # 不可删本地

    def test_test_connection_bad_host(self, admin):
        r = admin.post("/api/service-monitor/targets/test", json={
            "host": "bad host!", "username": "root", "password": "p"})
        data = r.get_json()
        assert data["success"] is False


# ============================================================
# 报告查看（普通用户可看）
# ============================================================

class TestReportAPI:

    def _make_report(self):
        target = {"id": "local", "name": "本机", "type": "local"}
        modules = [{"id": "m1", "name": "CPU检测", "category": "system",
                    "items": [{"key": "CPU_USAGE", "name": "CPU使用率", "value": "92%",
                               "threshold": ">85%", "status": "严重"}],
                    "summary": {"正常": 0, "警告": 0, "严重": 1, "total": 1}}]
        return report_service.save(target, "quick", modules, "2026-07-16T10:00:00")

    def test_list_reports_user_ok(self, user):
        self._make_report()
        r = user.get("/api/service-monitor/reports")
        assert r.status_code == 200
        assert len(r.get_json()["reports"]) == 1

    def test_get_report_user_ok(self, user):
        rid = self._make_report()
        r = user.get(f"/api/service-monitor/reports/{rid}")
        assert r.status_code == 200
        assert r.get_json()["report"]["summary"]["严重"] == 1

    def test_get_report_not_found(self, user):
        assert user.get("/api/service-monitor/reports/nonexistent").status_code == 404

    def test_export_md(self, user):
        rid = self._make_report()
        r = user.get(f"/api/service-monitor/reports/{rid}/export?format=md")
        assert r.status_code == 200
        assert "服务巡检报告".encode() in r.data

    def test_delete_report_admin_only(self, user, admin):
        rid = self._make_report()
        assert user.delete(f"/api/service-monitor/reports/{rid}").status_code == 403
        assert admin.delete(f"/api/service-monitor/reports/{rid}").status_code == 200

    def test_report_page_user_ok(self, user):
        rid = self._make_report()
        assert user.get(f"/service-monitor/report/{rid}").status_code == 200


# ============================================================
# 修复预留
# ============================================================

class TestFixReserved:

    def test_fix_admin_only(self, user, admin):
        assert user.post("/api/service-monitor/fix").status_code == 403
        r = admin.post("/api/service-monitor/fix")
        data = r.get_json()
        assert "开发中" in data.get("message", "") or data["success"] is False
