"""Mail API 集成测试 — 覆盖全部端点 + 防御逻辑 A1-A10"""

import json
import os
from pathlib import Path

import pytest
from fastapi.testclient import TestClient

from src.main import app

pytestmark = pytest.mark.integration

# 有效 Agent（与 mail_routes fallback 一致）
VALID_AGENTS = [
    "zhangfei-dev", "guanyu-dev", "zhaoyun-data",
    "jiangwei-infra", "pangtong-fujunshi", "simayi-challenger",
]
FROM_AGENT = "zhangfei-dev"
TO_AGENT = "simayi-challenger"
OTHER_AGENT = "pangtong-fujunshi"


@pytest.fixture
def mail_env(tmp_path):
    """创建临时 Mail 环境，确保 BLACKBOARD_ROOT 隔离"""
    mail_root = tmp_path / "mail_root"
    mail_root.mkdir()
    os.environ["BLACKBOARD_ROOT"] = str(mail_root)
    yield mail_root
    del os.environ["BLACKBOARD_ROOT"]


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


def _send(client, from_a: str = FROM_AGENT, to_a: str = TO_AGENT,
          title: str = "Test Mail", text: str = "Hello",
          **kwargs) -> dict:
    """发送邮件的便捷方法"""
    body = {"from": from_a, "to": to_a, "title": title, "text": text}
    body.update(kwargs)
    resp = client.post("/api/mail", json=body)
    return resp


# ===================================================================
# TestMailList
# ===================================================================

class TestMailList:
    def test_list_returns_all(self, client, mail_env):
        _send(client, title="Mail A")
        _send(client, from_a=TO_AGENT, to_a=FROM_AGENT, title="Mail B")
        resp = client.get("/api/mail")
        assert resp.status_code == 200
        data = resp.json()
        assert data["total"] == 2

    def test_filter_by_from_agent(self, client, mail_env):
        _send(client, title="From ZF")
        _send(client, from_a=OTHER_AGENT, to_a=TO_AGENT, title="From PT")
        resp = client.get("/api/mail", params={"from_agent": FROM_AGENT})
        assert resp.status_code == 200
        data = resp.json()
        assert data["total"] == 1
        assert data["mails"][0]["from"] == FROM_AGENT

    def test_filter_by_to_agent(self, client, mail_env):
        _send(client, title="To SMY")
        _send(client, from_a=TO_AGENT, to_a=OTHER_AGENT, title="To PT")
        resp = client.get("/api/mail", params={"to_agent": TO_AGENT})
        assert resp.status_code == 200
        data = resp.json()
        assert data["total"] == 1
        assert data["mails"][0]["to"] == TO_AGENT

    def test_filter_unread(self, client, mail_env):
        r = _send(client, title="Unread")
        mail_id = r.json()["mail_id"]
        # 标记已读
        client.patch(f"/api/mail/{mail_id}", json={"is_read": True})
        # 再发一封未读
        _send(client, from_a=OTHER_AGENT, to_a=FROM_AGENT, title="Still unread")
        resp = client.get("/api/mail", params={"unread": True})
        assert resp.status_code == 200
        data = resp.json()
        assert data["total"] == 1

    def test_limit_parameter(self, client, mail_env):
        for i in range(5):
            _send(client, title=f"Mail {i}")
        resp = client.get("/api/mail", params={"limit": 2})
        assert resp.status_code == 200
        assert resp.json()["total"] == 2


# ===================================================================
# TestMailAgents
# ===================================================================

class TestMailAgents:
    def test_agents_list(self, client, mail_env):
        _send(client, title="A")
        _send(client, from_a=OTHER_AGENT, to_a=TO_AGENT, title="B")
        resp = client.get("/api/mail/agents/list")
        assert resp.status_code == 200
        agents = resp.json()["agents"]
        assert FROM_AGENT in agents
        assert TO_AGENT in agents
        assert OTHER_AGENT in agents


# ===================================================================
# TestMailSummary
# ===================================================================

class TestMailSummary:
    def test_summary_fields(self, client, mail_env):
        _send(client, title="Info", type="inform")
        _send(client, title="Req", type="request")
        resp = client.get("/api/mail/summary")
        assert resp.status_code == 200
        data = resp.json()
        assert data["total"] == 2
        assert data["unread"] == 2
        assert data["by_type"]["inform"] == 1
        assert data["by_type"]["request"] == 1


# ===================================================================
# TestMailDetail
# ===================================================================

class TestMailDetail:
    def test_get_detail(self, client, mail_env):
        r = _send(client, title="Detail Test", text="Body content")
        mail_id = r.json()["mail_id"]
        resp = client.get(f"/api/mail/{mail_id}")
        assert resp.status_code == 200
        data = resp.json()
        assert data["title"] == "Detail Test"
        assert data["description"] == "Body content"
        assert data["from"] == FROM_AGENT
        assert data["to"] == TO_AGENT
        assert "comments" in data

    def test_detail_404(self, client, mail_env):
        resp = client.get("/api/mail/nonexistent-mail-id")
        assert resp.status_code == 404


# ===================================================================
# TestSendMail — A1-A10 防御逻辑
# ===================================================================

class TestSendMail:
    # --- 防御逻辑 ---

    def test_A1_from_missing(self, client, mail_env):
        """A1: from 必填 → 400"""
        resp = client.post("/api/mail", json={
            "to": TO_AGENT, "title": "T", "text": "Hi",
        })
        assert resp.status_code == 400
        assert "from" in resp.json()["detail"]

    def test_A9_from_invalid(self, client, mail_env):
        """A9: from 不是有效 Agent → 400"""
        resp = client.post("/api/mail", json={
            "from": "unknown-agent", "to": TO_AGENT, "title": "T", "text": "Hi",
        })
        assert resp.status_code == 400
        assert "不是有效的 Agent" in resp.json()["detail"]

    def test_A2_to_missing_non_reply(self, client, mail_env):
        """A2: 非回复时 to 缺失 → 400"""
        resp = client.post("/api/mail", json={
            "from": FROM_AGENT, "title": "T", "text": "Hi",
        })
        assert resp.status_code == 400
        assert "to" in resp.json()["detail"]

    def test_A3_from_equals_to(self, client, mail_env):
        """A3: from == to 防自环 → 400"""
        resp = client.post("/api/mail", json={
            "from": FROM_AGENT, "to": FROM_AGENT, "title": "T", "text": "Self",
        })
        assert resp.status_code == 400
        assert "不能给自己发邮件" in resp.json()["detail"]

    def test_A4_to_invalid(self, client, mail_env):
        """A4: to 不是有效 Agent → 400"""
        resp = client.post("/api/mail", json={
            "from": FROM_AGENT, "to": "nobody", "title": "T", "text": "Hi",
        })
        assert resp.status_code == 400
        assert "不是有效的 Agent" in resp.json()["detail"]

    def test_A10_text_empty(self, client, mail_env):
        """A10: 正文为空 → 400"""
        resp = client.post("/api/mail", json={
            "from": FROM_AGENT, "to": TO_AGENT, "title": "T", "text": "   ",
        })
        assert resp.status_code == 400
        assert "正文" in resp.json()["detail"]

    def test_A10_description_empty(self, client, mail_env):
        """A10: description 字段为空也触发 → 400"""
        resp = client.post("/api/mail", json={
            "from": FROM_AGENT, "to": TO_AGENT, "title": "T",
        })
        assert resp.status_code == 400

    # --- 正常发送 ---

    def test_send_inform(self, client, mail_env):
        """正常发送 inform → 200"""
        resp = _send(client, type="inform")
        assert resp.status_code == 200
        data = resp.json()
        assert data["ok"]
        assert data["mail_id"]
        # 验证列表可见
        mails = client.get("/api/mail").json()["mails"]
        assert len(mails) == 1
        assert mails[0]["type"] == "inform"

    def test_send_request(self, client, mail_env):
        """正常发送 request → 200"""
        resp = _send(client, type="request")
        assert resp.status_code == 200
        mails = client.get("/api/mail").json()["mails"]
        assert mails[0]["type"] == "request"

    # --- 回复逻辑 ---

    def test_A5_reply_to_nonexistent(self, client, mail_env):
        """A5: in_reply_to 不存在 → 400"""
        resp = _send(client, in_reply_to="mail-99999")
        assert resp.status_code == 400
        assert "不存在" in resp.json()["detail"]

    def test_A6_A7_auto_correct_to(self, client, mail_env):
        """A6/A7: 回复时 to 被自动纠正为原发件者 → 200 + auto_corrected"""
        # simayi → zhangfei
        r = _send(client, from_a=TO_AGENT, to_a=FROM_AGENT,
                  title="Original", text="Original body")
        orig_id = r.json()["mail_id"]

        # zhangfei 回复 simayi，但故意传错 to
        resp = _send(client, from_a=FROM_AGENT, to_a=OTHER_AGENT,
                     title="Reply", text="Reply body",
                     in_reply_to=orig_id)
        assert resp.status_code == 200
        data = resp.json()
        assert data["ok"]
        assert "auto_corrected" in data
        assert data["auto_corrected"]["field"] == "to"
        assert data["auto_corrected"]["original"] == OTHER_AGENT
        assert data["auto_corrected"]["corrected"] == TO_AGENT

    def test_A8_non_party_reply_rejected(self, client, mail_env):
        """A8: 非邮件双方回复 → 400"""
        # zhangfei → simayi
        r = _send(client, title="Private", text="Secret")
        orig_id = r.json()["mail_id"]

        # pangtong（第三方）尝试回复
        resp = _send(client, from_a=OTHER_AGENT, to_a=FROM_AGENT,
                     title="Intrude", text="I shouldn't be here",
                     in_reply_to=orig_id)
        assert resp.status_code == 400
        assert "发送者或接收者" in resp.json()["detail"]

    def test_reply_inherits_conversation_id(self, client, mail_env):
        """回复时 conversation_id 继承"""
        r = _send(client, from_a=TO_AGENT, to_a=FROM_AGENT,
                  title="Conv Start", text="Start",
                  conversation_id="conv-test-123")
        orig_id = r.json()["mail_id"]

        resp = _send(client, from_a=FROM_AGENT, to_a=TO_AGENT,
                     title="Conv Reply", text="Reply",
                     in_reply_to=orig_id)
        assert resp.status_code == 200
        reply_id = resp.json()["mail_id"]

        # 验证回复邮件继承了 conversation_id
        detail = client.get(f"/api/mail/{reply_id}").json()
        assert detail["conversation_id"] == "conv-test-123"

    def test_reply_type_defaults_inform(self, client, mail_env):
        """回复时 type 默认 inform"""
        r = _send(client, from_a=TO_AGENT, to_a=FROM_AGENT,
                  title="Orig", text="Hi", type="request")
        orig_id = r.json()["mail_id"]

        # 不传 type，应默认 inform
        resp = client.post("/api/mail", json={
            "from": FROM_AGENT, "to": TO_AGENT,
            "title": "Re: Orig", "text": "Reply",
            "in_reply_to": orig_id,
        })
        assert resp.status_code == 200
        reply_id = resp.json()["mail_id"]
        detail = client.get(f"/api/mail/{reply_id}").json()
        assert detail["type"] == "inform"


# ===================================================================
# TestDeleteMail
# ===================================================================

class TestDeleteMail:
    def test_delete_by_prefix(self, client, mail_env):
        _send(client, title="[BULK] Alert 1")
        _send(client, title="[BULK] Alert 2")
        _send(client, title="Keep this")
        resp = client.delete("/api/mail", params={"prefix": "[BULK]"})
        assert resp.status_code == 200
        data = resp.json()
        assert data["ok"]
        assert data["deleted_count"] == 2
        # deleted_count=2 表示成功；被删除的邮件 status=cancelled
        # 列表默认不按 status 过滤，所以验证非 cancelled 的只有 1 封
        mails = client.get("/api/mail").json()["mails"]
        active = [m for m in mails if m["status"] != "cancelled"]
        assert len(active) == 1
        assert active[0]["title"] == "Keep this"

    def test_delete_prefix_missing(self, client, mail_env):
        resp = client.delete("/api/mail")
        assert resp.status_code == 400

    def test_delete_no_match(self, client, mail_env):
        _send(client, title="Keep")
        resp = client.delete("/api/mail", params={"prefix": "NONEXISTENT"})
        assert resp.status_code == 200
        assert resp.json()["deleted_count"] == 0


# ===================================================================
# TestUpdateMail
# ===================================================================

class TestUpdateMail:
    def test_mark_read(self, client, mail_env):
        r = _send(client, title="Read me")
        mail_id = r.json()["mail_id"]

        resp = client.patch(f"/api/mail/{mail_id}", json={"is_read": True})
        assert resp.status_code == 200
        assert resp.json()["ok"]

        # 验证已读
        detail = client.get(f"/api/mail/{mail_id}").json()
        assert detail["is_read"] is True

    def test_mark_executed(self, client, mail_env):
        r = _send(client, title="Do me")
        mail_id = r.json()["mail_id"]

        resp = client.patch(f"/api/mail/{mail_id}", json={"mark_executed": True})
        assert resp.status_code == 200
        assert resp.json()["ok"]

        # mark_executed 设 is_read=True 并尝试 status→done
        # pending→done 不是合法转换，但 is_read 应被设为 True
        detail = client.get(f"/api/mail/{mail_id}").json()
        assert detail["is_read"] is True

    def test_update_404(self, client, mail_env):
        resp = client.patch("/api/mail/no-such-mail", json={"is_read": True})
        assert resp.status_code == 404
