"""API 路由 — Mail Tab（v2.7）

Mail 是一个特殊的 Project (_mail)，每封 Mail 是一个两点 Task（from → to）。
显示为独立 Tab，list 形式展示：时间 | From | To | Title | 状态（已读/已执行）。
"""

from __future__ import annotations

import json
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, Optional

from fastapi import APIRouter, HTTPException, Query

from src.blackboard.db import init_db
from src.blackboard.models import Task
from src.blackboard.operations import Blackboard
from src.blackboard.queries import Queries
from src.utils import get_data_root


def _get_valid_agents() -> set:
    """从 config/default.yaml 读取有效 Agent ID 列表"""
    config_path = Path(__file__).parent.parent / "config" / "default.yaml"
    if config_path.exists():
        import yaml
        try:
            with open(config_path) as f:
                cfg = yaml.safe_load(f)
            profiles = cfg.get("daemon", {}).get("agent_profiles", {})
            if profiles:
                return set(profiles.keys())
        except Exception:
            pass
    # fallback：硬编码
    return {"zhangfei-dev", "guanyu-dev", "zhaoyun-data",
            "jiangwei-infra", "pangtong-fujunshi", "simayi-challenger"}


router = APIRouter(prefix="/api/mail", tags=["mail"])

MAIL_PROJECT_ID = "_mail"


def _db_path() -> Path:
    root = get_data_root()
    db = root / MAIL_PROJECT_ID / "blackboard.db"
    db.parent.mkdir(parents=True, exist_ok=True)
    init_db(db)
    return db


def _bb() -> Blackboard:
    return Blackboard(_db_path())


def _q() -> Queries:
    return Queries(_db_path())


def _mail_meta(task: Task) -> Dict[str, Any]:
    """从 Task 的 must_haves 字段解析 Mail 元数据"""
    if task.must_haves:
        try:
            return json.loads(task.must_haves)
        except (json.JSONDecodeError, TypeError):
            pass
    return {}


def _task_to_mail(task: Task) -> Dict[str, Any]:
    """Task → Mail JSON"""
    meta = _mail_meta(task)
    return {
        "id": task.id,
        "title": task.title,
        "from": task.assigned_by or "unknown",
        "to": task.assignee,
        "status": task.status,
        "type": meta.get("type", "inform"),
        "performative": meta.get("performative", "inform"),
        "is_read": meta.get("is_read", False),
        "created_at": task.created_at,
        "updated_at": task.updated_at,
        "description": task.description,
        "conversation_id": meta.get("conversation_id"),
        "in_reply_to": meta.get("in_reply_to"),
    }


@router.get("")
async def list_mail(
    status: Optional[str] = None,
    from_agent: Optional[str] = None,
    to_agent: Optional[str] = None,
    unread: Optional[bool] = None,
    limit: int = Query(50, le=200),
):
    """Mail 列表（按时间倒序）"""
    bb = _bb()
    tasks = bb.list_tasks(
        status=status,
        assignee=to_agent,
        assigned_by=from_agent)

    mails = []
    for t in tasks:
        meta = _mail_meta(t)
        # unread 过滤（must_haves JSON 内字段，无法 SQL WHERE）
        if unread is True and meta.get("is_read", False):
            continue
        mails.append(_task_to_mail(t))

    # 按时间倒序
    mails.sort(key=lambda m: m.get("created_at", ""), reverse=True)
    mails = mails[:limit]

    return {"mails": mails, "total": len(mails)}


@router.get("/agents/list")
async def list_mail_agents():
    """列出参与过 Mail 的所有 Agent（用于筛选）"""
    q = _q()
    conn = q._conn()
    try:
        senders = conn.execute(
            "SELECT DISTINCT assigned_by FROM tasks WHERE assigned_by IS NOT NULL"
        ).fetchall()
        receivers = conn.execute(
            "SELECT DISTINCT assignee FROM tasks WHERE assignee IS NOT NULL"
        ).fetchall()
        agents = list(set(
            [r["assigned_by"] for r in senders] +
            [r["assignee"] for r in receivers]
        ))
        agents.sort()
        return {"agents": agents}
    finally:
        conn.close()


@router.get("/summary")
async def mail_summary():
    """Mail 摘要（未读数、总数）"""
    bb = _bb()
    all_tasks = bb.list_tasks()

    total = len(all_tasks)
    unread = 0
    by_type: Dict[str, int] = {}

    for t in all_tasks:
        meta = _mail_meta(t)
        if not meta.get("is_read", False):
            unread += 1
        mtype = meta.get("type", "inform")
        by_type[mtype] = by_type.get(mtype, 0) + 1

    return {"total": total, "unread": unread, "by_type": by_type}


@router.get("/{mail_id}")
async def get_mail(mail_id: str):
    """Mail 详情"""
    bb = _bb()
    task = bb.get_task(mail_id)
    if not task:
        raise HTTPException(404, f"Mail not found: {mail_id}")

    result = _task_to_mail(task)
    # 附加 comments（用于对话线程）
    comments = bb.get_comments(mail_id)
    result["comments"] = [
        {
            "id": c.id,
            "author": c.author,
            "type": c.comment_type,
            "body": c.body,
            "created_at": c.created_at,
        }
        for c in comments
    ]
    return result


@router.post("")
async def send_mail(body: Dict[str, Any]):
    """发送 Mail（创建 Task）

    API 层防御（A1-A10）：
    A1: from 必填
    A9: from 必须是有效 Agent
    A5: in_reply_to 存在性校验
    A6/A7: 自动纠正 to
    A2: to 必填（非回复）
    A3: from != to 防自环
    A4: to 必须是有效 Agent
    A8: 回复权限校验（严格 1 对 1）
    A10: 正文非空
    """
    bb = _bb()
    valid_agents = _get_valid_agents()
    auto_corrected = None

    # --- A1: from 必填 ---
    from_agent = body.get("from", "").strip()
    if not from_agent:
        raise HTTPException(400, "`from` 必填")

    # --- A9: from 必须是有效 Agent ---
    if from_agent not in valid_agents:
        raise HTTPException(400, f"`from` 不是有效的 Agent: {from_agent}")

    # --- A5/A6/A7/A8: in_reply_to 处理 ---
    in_reply_to = body.get("in_reply_to")
    original = None
    if in_reply_to:
        original = bb.get_task(in_reply_to)
        # A5: 原邮件必须存在
        if not original:
            raise HTTPException(400, f"回复的邮件不存在: {in_reply_to}")

        orig_from = original.assigned_by or ""
        orig_to = original.assignee or ""

        # A8: 只有原邮件的双方能回复（严格 1 对 1）
        if from_agent not in (orig_from, orig_to):
            raise HTTPException(400, "只有邮件的发送者或接收者可以回复")

        # A6/A7: 自动纠正 to → 原邮件发件者
        to_agent = body.get("to", "").strip()
        corrected_to = orig_from  # 回复方向固定: reply → original sender
        if to_agent and to_agent != corrected_to:
            auto_corrected = {
                "field": "to",
                "original": to_agent,
                "corrected": corrected_to}
        to_agent = corrected_to
    else:
        # --- A2: to 必填（非回复场景） ---
        to_agent = body.get("to", "").strip()
        if not to_agent:
            raise HTTPException(400, "`to` 必填")

    # --- A3: from != to 防自环 ---
    if from_agent == to_agent:
        raise HTTPException(400, "不能给自己发邮件")

    # --- A4: to 必须是有效 Agent ---
    if to_agent not in valid_agents:
        raise HTTPException(400, f"`to` 不是有效的 Agent: {to_agent}")

    # --- A10: 正文非空 ---
    text = body.get("text", body.get("description", "")) or ""
    if not text.strip():
        raise HTTPException(400, "邮件正文不能为空")

    mail_id = body.get("id", f"mail-{int(datetime.now().timestamp() * 1000)}")

    # 自动处理 conversation_id：有 in_reply_to 时继承原邮件的 conversation_id
    conversation_id = body.get("conversation_id")
    if not conversation_id and original:
        try:
            orig_meta = json.loads(
                original.must_haves) if original.must_haves else {}
            conversation_id = orig_meta.get("conversation_id")
        except Exception:
            pass
    if not conversation_id:
        conversation_id = f"conv-{int(datetime.now().timestamp() * 1000)}"

    # 自动处理 type：有 in_reply_to 且未显式指定 type 时默认 inform（防循环）
    mail_type = body.get("type")
    if mail_type is None:
        mail_type = "inform" if in_reply_to else "request"

    # performative 与 type 同义，type 优先
    performative = body.get("performative", mail_type)

    meta = {
        "type": mail_type,
        "performative": performative,
        "is_read": False,
        "conversation_id": conversation_id,
        "in_reply_to": in_reply_to,
        "from": from_agent,
    }

    task = Task(
        id=mail_id,
        title=body.get("title", ""),
        description=text,
        assignee=to_agent,
        assigned_by=from_agent,
        must_haves=json.dumps(meta),
        task_type="mail",
        status="pending",
    )
    bb.create_task(task)

    result = {"ok": True, "mail_id": task.id}
    if auto_corrected:
        result["auto_corrected"] = auto_corrected
    return result


@router.delete("")
async def delete_mail(prefix: Optional[str] = Query(None)):
    """批量删除邮件（按标题前缀匹配，status→cancelled）"""
    if not prefix:
        raise HTTPException(400, "`prefix` 查询参数必填")

    bb = _bb()
    tasks = bb.list_tasks()

    deleted_ids = []
    for t in tasks:
        if t.title and t.title.startswith(prefix):
            if t.status not in ("cancelled",):
                bb.update_task_status(
                    t.id, "cancelled", agent="mail-cleanup-api")
                deleted_ids.append(t.id)

    return {"ok": True, "deleted_count": len(
        deleted_ids), "deleted_ids": deleted_ids}


@router.patch("/{mail_id}")
async def update_mail(mail_id: str, body: Dict[str, Any]):
    """更新 Mail（标记已读/已执行）"""
    bb = _bb()
    task = bb.get_task(mail_id)
    if not task:
        raise HTTPException(404, f"Mail not found: {mail_id}")

    meta = _mail_meta(task)

    if "is_read" in body:
        meta["is_read"] = body["is_read"]

    if body.get("mark_executed"):
        meta["is_read"] = True
        if task.status not in ("done", "cancelled"):
            bb.update_task_status(mail_id, "done", agent="mail-api")

    bb.update_must_haves(mail_id, json.dumps(meta))

    return {"ok": True}
