# -*- coding: utf-8 -*-
"""FastMCP 服务入口：只做装配（校验入参 -> 调 core -> 返回 dict），不含业务逻辑。

启动：fastmcp dev src/a207_followup_mcp/server.py
设计要点：随访计划摘要可见、原始医生备注仅临床角色可见；PEW 时间线 facde 接受 M3 输出（ADR-007）。
调用方身份来自部署注入的环境变量 A207_CALLER，工具签名不暴露 caller（P0-1）。
"""
from __future__ import annotations

from typing import Any

from fastmcp import FastMCP

from a207_policy import CallerError

from . import core
from .models import (
    AdherenceRequest,
    GetFollowupRequest,
    PewTimelineFacadeRequest,
    ScheduleFollowupRequest,
)

mcp = FastMCP("a207-followup-mcp")


def _invalid(exc: Exception) -> dict[str, Any]:
    if isinstance(exc, CallerError):
        # 权限/身份错误（PermissionDenied / CallerUnknown）不可错标为 INVALID_INPUT，
        # 原样上抛由 FastMCP 暴露为 DENIED / CallerUnknown，便于上层与红队正确识别。
        raise
    return {"ok": False, "error": "INVALID_INPUT", "detail": str(exc)}


@mcp.tool
def schedule_followup(patient_id: str, ckd_stage: str, anchor_date: str,
                      plan_summary: str, albuminuria_stage: str = "A1",
                      visit_type: str = "outpatient",
                      note_to_clinician: str = "") -> dict[str, Any]:
    """创建随访计划（写，MX 收口：仅医生/营养师/编排层）。频率按 KDIGO 2024 儿科推荐自动计算。

    身份来自部署注入的环境变量 A207_CALLER（模型不可自证）。
    ckd_stage=G1/G2/G3a/G3b/G4/G5/G5D；anchor_date=本次就诊日 YYYY-MM-DD；plan_summary 摘要所有角色可见；
    note_to_clinician 仅医生/营养可见。返回含 next_due_date 的计划。
    """
    try:
        req: ScheduleFollowupRequest = ScheduleFollowupRequest(
            patient_id=patient_id, ckd_stage=ckd_stage, albuminuria_stage=albuminuria_stage,
            visit_type=visit_type, anchor_date=anchor_date,
            plan_summary=plan_summary, note_to_clinician=note_to_clinician)
    except Exception as exc:
        return _invalid(exc)
    return core.schedule_followup(
        patient_id=req.patient_id, ckd_stage=req.ckd_stage, albuminuria_stage=req.albuminuria_stage,
        visit_type=req.visit_type, anchor_date=req.anchor_date,
        plan_summary=req.plan_summary, note_to_clinician=req.note_to_clinician)


@mcp.tool
def get_followup_records(patient_id: str) -> dict[str, Any]:
    """读取随访记录与计划时间线（读，所有角色可读）。

    身份来自部署注入的环境变量 A207_CALLER（模型不可自证）。
    临床角色（医生/营养/编排/风险）看完整记录（含原始医生备注）；患者/家属角色仅见摘要——医生备注被剔除。
    """
    try:
        req: GetFollowupRequest = GetFollowupRequest(patient_id=patient_id)
    except Exception as exc:
        return _invalid(exc)
    return core.get_followup_records(req.patient_id)


@mcp.tool
def get_adherence_score(patient_id: str, diet_ratio: float, med_ratio: float,
                        visit_ratio: float) -> dict[str, Any]:
    """计算并记录依从性评分（M4 拥有依从性数据）。diet/med/visit 三域比率(0-1) → 加权复合分(0-100)。

    身份来自部署注入的环境变量 A207_CALLER（模型不可自证）。
    等级：≥80 好 / 50-79 中 / <50 差。默认等权（饮食/用药/就诊各 1/3，待临床验证）。
    """
    try:
        req: AdherenceRequest = AdherenceRequest(
            patient_id=patient_id, diet_ratio=diet_ratio, med_ratio=med_ratio,
            visit_ratio=visit_ratio)
    except Exception as exc:
        return _invalid(exc)
    return core.get_adherence_score(
        patient_id=req.patient_id, diet_ratio=req.diet_ratio, med_ratio=req.med_ratio,
        visit_ratio=req.visit_ratio)


@mcp.tool
def get_pew_timeline(patient_id: str, pew_history: list[dict] | None = None) -> dict[str, Any]:
    """PEW 时间线聚合 facade（ADR-007：存储归属 M3）。

    身份来自部署注入的环境变量 A207_CALLER（模型不可自证）。
    接受 M3 get_pew_history 的输出（list of {date, score, level}），并入统一随访时间线。零跨包 import。
    """
    try:
        req: PewTimelineFacadeRequest = PewTimelineFacadeRequest(
            patient_id=patient_id, pew_history=pew_history or [])
    except Exception as exc:
        return _invalid(exc)
    return core.get_pew_timeline(req.patient_id, req.pew_history)


if __name__ == "__main__":
    mcp.run()
