"""Pure posture metric helpers — testable without MediaPipe/OpenCV."""

from __future__ import annotations

import math
from statistics import mean, pstdev

# MediaPipe Pose landmark indices
NOSE = 0
LEFT_SHOULDER = 11
RIGHT_SHOULDER = 12
LEFT_HIP = 23
RIGHT_HIP = 24

Landmark = dict[str, float]
FrameLandmarks = dict[int, Landmark]

from app.ai.calibration import CALIBRATION, METRIC_NAMES


def _point(frame: FrameLandmarks, idx: int) -> Landmark | None:
    point = frame.get(idx)
    if not point or point.get("visibility", 0) < 0.5:
        return None
    return point


def _shoulder_width(frame: FrameLandmarks) -> float | None:
    left = _point(frame, LEFT_SHOULDER)
    right = _point(frame, RIGHT_SHOULDER)
    if not left or not right:
        return None
    width = abs(right["x"] - left["x"])
    return width if width > 1e-4 else None


def score_head_forward(frame: FrameLandmarks) -> float | None:
    nose = _point(frame, NOSE)
    left = _point(frame, LEFT_SHOULDER)
    right = _point(frame, RIGHT_SHOULDER)
    width = _shoulder_width(frame)
    if not nose or not left or not right or not width:
        return None
    mid_x = (left["x"] + right["x"]) / 2
    offset = abs(nose["x"] - mid_x) / width
    return min(100.0, max(0.0, offset * CALIBRATION["head_forward_scale"]))


def score_shoulder_asymmetry(frame: FrameLandmarks) -> float | None:
    left = _point(frame, LEFT_SHOULDER)
    right = _point(frame, RIGHT_SHOULDER)
    width = _shoulder_width(frame)
    if not left or not right or not width:
        return None
    diff = abs(left["y"] - right["y"]) / width
    return min(100.0, max(0.0, diff * CALIBRATION["shoulder_asymmetry_scale"]))


def score_pelvic_tilt(frame: FrameLandmarks) -> float | None:
    left = _point(frame, LEFT_HIP)
    right = _point(frame, RIGHT_HIP)
    if not left or not right:
        return None
    angle = abs(math.degrees(math.atan2(right["y"] - left["y"], right["x"] - left["x"])))
    tilt = min(angle, 180 - angle)
    return min(100.0, max(0.0, tilt * CALIBRATION["pelvic_tilt_scale"]))


def value_to_level(value: float) -> str:
    if value < CALIBRATION["normal_max"]:
        return "normal"
    if value < CALIBRATION["low_max"]:
        return "low"
    if value < CALIBRATION["medium_max"]:
        return "medium"
    return "high"


def aggregate_metric(values: list[float]) -> tuple[float, str, float]:
    avg = mean(values)
    level = value_to_level(avg)
    confidence = min(0.98, 0.72 + min(len(values), 24) * 0.01)
    return round(avg, 1), level, round(confidence, 2)


def build_screening_report(frames: list[FrameLandmarks]) -> dict:
    head_vals: list[float] = []
    shoulder_vals: list[float] = []
    pelvic_vals: list[float] = []

    for frame in frames:
        head = score_head_forward(frame)
        shoulder = score_shoulder_asymmetry(frame)
        pelvic = score_pelvic_tilt(frame)
        if head is not None:
            head_vals.append(head)
        if shoulder is not None:
            shoulder_vals.append(shoulder)
        if pelvic is not None:
            pelvic_vals.append(pelvic)

    if not head_vals and not shoulder_vals and not pelvic_vals:
        raise ValueError("insufficient_pose_frames")

    metrics = []
    for name, values in zip(METRIC_NAMES, (head_vals, shoulder_vals, pelvic_vals), strict=True):
        if not values:
            continue
        value, level, confidence = aggregate_metric(values)
        metrics.append({"name": name, "value": value, "level": level, "confidence": confidence})

    worst = max((m["value"] for m in metrics), default=0)
    risk_level = value_to_level(worst)
    if worst >= CALIBRATION["review_required_min"]:
        risk_level = "review_required"
    if risk_level == "normal":
        summary = "体态指标整体正常，建议保持日常活动与姿势习惯"
    elif risk_level == "low":
        summary = "存在轻度体态偏差，建议开始基础纠正训练"
    elif risk_level == "medium":
        summary = "建议关注头前伸与高低肩，开始针对性训练"
    elif risk_level == "review_required":
        summary = "指标偏高，已提交机构教练复核"
    else:
        summary = "多项指标偏高，建议尽快安排专业评估与干预"

    recommendations = []
    head_metric = next((m for m in metrics if m["name"] == "头前伸"), None)
    shoulder_metric = next((m for m in metrics if m["name"] == "高低肩"), None)
    if head_metric and head_metric["level"] in ("medium", "high"):
        recommendations.append("每日肩胛稳定训练 5 分钟")
        recommendations.append("颈后肌群拉伸 3 组")
    if shoulder_metric and shoulder_metric["level"] in ("medium", "high"):
        recommendations.append("对称性肩带激活训练 2 组")
    if not recommendations:
        recommendations.append("保持每日 20 分钟户外活动")
    recommendations.append("28 天后建议复测对比")

    return {
        "riskLevel": risk_level,
        "summary": summary,
        "metrics": metrics,
        "recommendations": recommendations,
        "disclaimer": "本报告用于健康管理建议，不构成医疗诊断。",
    }


def build_movement_report(frames: list[FrameLandmarks]) -> dict:
    if len(frames) < 3:
        raise ValueError("insufficient_pose_frames")

    nose_y = []
    shoulder_angles = []
    for frame in frames:
        nose = _point(frame, NOSE)
        left = _point(frame, LEFT_SHOULDER)
        right = _point(frame, RIGHT_SHOULDER)
        if nose:
            nose_y.append(nose["y"])
        if left and right:
            shoulder_angles.append(
                math.degrees(math.atan2(right["y"] - left["y"], right["x"] - left["x"]))
            )

    stability = 90.0
    if len(nose_y) >= 3:
        stability = max(55.0, 100.0 - pstdev(nose_y) * 900)

    angle_score = 85.0
    if len(shoulder_angles) >= 3:
        angle_score = max(50.0, 100.0 - pstdev(shoulder_angles) * 2.5)

    rhythm = min(100.0, 70 + len(frames) * 1.2)
    trajectory = min(100.0, stability + 5)
    completion = min(100.0, 60 + len(frames) * 2)
    score = round((trajectory + angle_score + rhythm + stability + completion) / 5)

    return {
        "taskType": "movement_scoring",
        "score": score,
        "repsCompleted": max(1, len(frames) // 4),
        "durationSeconds": len(frames) * 8,
        "dimensions": {
            "trajectory": round(trajectory),
            "angle": round(angle_score),
            "rhythm": round(rhythm),
            "stability": round(stability),
            "completion": round(completion),
        },
        "baselineCompare": {
            "headNeckAngle": {"screening": 21, "current": max(8, 21 - score // 10), "delta": -(score // 10)},
            "shoulderDiffMm": {"screening": 12, "current": max(3, 12 - score // 12), "delta": -(score // 12)},
        },
        "disclaimer": "本报告用于运动训练反馈，不构成医疗诊断。",
    }
