"""分析服务（4 端 Dashboard 聚合 + DataScope 注入 + 降级兜底）.

对齐 02-architecture-design.md §7 AnalyticsService：
  - GetTeacherDashboard：班级聚合（教师视角）
  - GetStudentDashboard：学生学情（学生视角）
  - GetParentDashboard：孩子学情（家长视角）
  - GetAdminDashboard：学校聚合 + AI 用量（管理员视角）

DataScope 注入：
  - 教师只能查自己班级数据（CLASS 级 scope_ids 过滤）
  - 学生只能查自己数据（SELF 级 user_id 过滤）
  - 管理员可查全校数据（ALL 级无过滤）

降级策略：
  - ClickHouse 不可达：返回骨架数据 + degraded=true
  - iam gRPC 不可达：使用 role-based fallback + degraded=true
"""

from typing import Any

import structlog

from .repository import clickhouse_repository, iam_client
from .shared.permissions import DataScopeLevel, UserContext

logger = structlog.get_logger(__name__)


def _skeleton_dashboard(dashboard_type: str, user_id: str, reason: str) -> dict[str, Any]:
    """降级骨架数据（ClickHouse 不可达时返回）."""
    return {
        "userId": user_id,
        "dashboardType": dashboard_type,
        "degraded": True,
        "degraded_reason": reason,
        "totalClasses": 0,
        "totalStudents": 0,
        "averageScore": 0.0,
        "passRate": 0.0,
        "weakStudents": [],
        "recentWarnings": [],
        "trend": [],
    }


async def get_teacher_dashboard(
    user: UserContext,
    class_id: str = "",
    subject_id: str = "",
) -> dict[str, Any]:
    """教师仪表盘（聚合班级成绩 + 薄弱学生 + 预警）.

    DataScope：
      - CLASS 级：仅返回 scope_ids 内的班级数据
      - ALL 级：返回所有班级数据
    """
    # 1. 解析 DataScope
    scope = await iam_client.get_effective_datascope(user)
    degraded = scope.degraded

    # 2. 若指定 class_id，验证是否在 scope 内
    effective_class_ids: list[str] = []
    if class_id:
        if scope.level == DataScopeLevel.CLASS and class_id not in scope.scope_ids:
            return {
                **_skeleton_dashboard("teacher", user.user_id, "datascope_violation"),
                "error": "class_id_out_of_scope",
            }
        effective_class_ids = [class_id]
    elif scope.level == DataScopeLevel.CLASS:
        effective_class_ids = scope.scope_ids

    # 3. 查询 ClickHouse
    if not effective_class_ids and scope.level != DataScopeLevel.ALL:
        # CLASS 级无 scope_ids，返回空骨架
        return _skeleton_dashboard("teacher", user.user_id, "no_class_scope")

    dashboard = await clickhouse_repository.query_teacher_dashboard(
        user_id=user.user_id,
        class_id=effective_class_ids[0] if effective_class_ids else "",
    )
    if dashboard is None:
        return _skeleton_dashboard("teacher", user.user_id, "clickhouse_unavailable")

    # 4. 补充薄弱学生列表
    weak_students: list[dict[str, Any]] = []
    if effective_class_ids:
        for cid in effective_class_ids[:5]:  # 限制 5 个班级
            class_perf = await clickhouse_repository.query_class_performance(
                class_id=cid,
                subject_id=subject_id,
            )
            if class_perf is not None:
                weak_students.append(
                    {
                        "class_id": cid,
                        "total_students": class_perf.get("totalStudents", 0),
                        "average_score": class_perf.get("averageScore", 0.0),
                        "pass_rate": class_perf.get("passRate", 0.0),
                    }
                )

    # 5. 补充降级标记
    if degraded:
        dashboard["degraded"] = True
        dashboard["degraded_reason"] = scope.degraded_reason or "iam_grpc_unavailable"

    dashboard["classes"] = weak_students
    dashboard["dashboardType"] = "teacher"
    return dashboard


async def get_student_dashboard(
    user: UserContext,
    student_id: str = "",
    subject_id: str = "",
) -> dict[str, Any]:
    """学生仪表盘（学情宽表 + 薄弱知识点 + 学习趋势）.

    DataScope：
      - SELF 级：student_id 必须为当前 user_id
      - CLASS/GRADE/SCHOOL/ALL 级：可查指定 student_id
    """
    # 1. 解析 DataScope
    scope = await iam_client.get_effective_datascope(user)
    degraded = scope.degraded

    # 2. SELF 级强制限制 student_id
    effective_student_id = student_id or user.user_id
    if scope.level == DataScopeLevel.SELF:
        effective_student_id = user.user_id  # SELF 级只能查自己

    # 3. 查询 ClickHouse
    dashboard = await clickhouse_repository.query_student_dashboard(effective_student_id)
    if dashboard is None:
        return _skeleton_dashboard("student", effective_student_id, "clickhouse_unavailable")

    # 4. 补充薄弱知识点
    weakness = await clickhouse_repository.query_student_weakness(
        student_id=effective_student_id,
        subject_id=subject_id,
    )
    dashboard["weakPoints"] = weakness.get("weakPoints", []) if weakness else []

    # 5. 补充学习趋势
    trend = await clickhouse_repository.query_learning_trend(
        student_id=effective_student_id,
        subject_id=subject_id,
    )
    dashboard["trend"] = trend.get("points", []) if trend else []

    # 6. 补充掌握度快照
    mastery = await clickhouse_repository.query_mastery_snapshot(
        student_id=effective_student_id,
        subject_id=subject_id,
    )
    dashboard["mastery"] = mastery if mastery else {"knowledgePoints": [], "overallMastery": 0.0}

    # 7. 降级标记
    if degraded:
        dashboard["degraded"] = True
        dashboard["degraded_reason"] = scope.degraded_reason or "iam_grpc_unavailable"

    dashboard["dashboardType"] = "student"
    return dashboard


async def get_parent_dashboard(
    user: UserContext,
    child_id: str = "",
) -> dict[str, Any]:
    """家长仪表盘（孩子学情聚合）.

    DataScope：
      - SELF 级：child_id 必须为家长关联的孩子（iam.GetChildrenByParent 校验）
      - 降级时使用 child_id 直接查询（依赖 Gateway 层鉴权）
    """
    # 1. 解析 DataScope
    scope = await iam_client.get_effective_datascope(user)
    degraded = scope.degraded

    # 2. 学生 ID（家长视角：使用 child_id 或 user_id）
    effective_student_id = child_id or user.user_id

    # 3. 查询 ClickHouse（复用学生仪表盘查询）
    dashboard = await clickhouse_repository.query_student_dashboard(effective_student_id)
    if dashboard is None:
        return _skeleton_dashboard("parent", effective_student_id, "clickhouse_unavailable")

    # 4. 补充考勤
    attendance = await clickhouse_repository.query_attendance(effective_student_id)
    dashboard["attendance"] = (
        attendance
        if attendance
        else {
            "absentCount": 0,
            "lateCount": 0,
            "presentCount": 0,
        }
    )

    # 5. 补充学习趋势
    trend = await clickhouse_repository.query_learning_trend(effective_student_id)
    dashboard["trend"] = trend.get("points", []) if trend else []

    # 6. 降级标记
    if degraded:
        dashboard["degraded"] = True
        dashboard["degraded_reason"] = scope.degraded_reason or "iam_grpc_unavailable"

    dashboard["dashboardType"] = "parent"
    dashboard["childId"] = effective_student_id
    return dashboard


async def get_admin_dashboard(
    user: UserContext,
    school_id: str = "",
) -> dict[str, Any]:
    """管理员仪表盘（学校聚合 + AI 用量统计）.

    DataScope：
      - SCHOOL 级：仅返回指定 school_id 数据
      - ALL 级：返回所有数据
    """
    # 1. 解析 DataScope
    scope = await iam_client.get_effective_datascope(user)
    degraded = scope.degraded

    # 2. 学校级聚合查询（复用 teacher_dashboard，不传 class_id）
    dashboard = await clickhouse_repository.query_teacher_dashboard(
        user_id=user.user_id,
        class_id="",
    )
    if dashboard is None:
        return _skeleton_dashboard("admin", user.user_id, "clickhouse_unavailable")

    # 3. 补充 AI 用量统计（P5 启用，对齐 02 §7.4）
    ai_usage = await clickhouse_repository.query_ai_usage_summary()
    dashboard["aiUsage"] = (
        ai_usage
        if ai_usage
        else {
            "totalRequests": 0,
            "totalTokens": 0,
            "totalCostCents": 0,
            "byProvider": [],
        }
    )

    # 4. 降级标记
    if degraded:
        dashboard["degraded"] = True
        dashboard["degraded_reason"] = scope.degraded_reason or "iam_grpc_unavailable"

    dashboard["dashboardType"] = "admin"
    dashboard["schoolId"] = school_id or scope.school_id
    return dashboard


async def get_class_performance(
    user: UserContext,
    class_id: str,
    subject_id: str = "",
    start_date: int = 0,
    end_date: int = 0,
) -> dict[str, Any]:
    """查询班级成绩分析（GetClassPerformance RPC 实现）.

    DataScope：CLASS 级需验证 class_id 在 scope_ids 内.
    """
    scope = await iam_client.get_effective_datascope(user)
    degraded = scope.degraded

    # DataScope 校验
    if scope.level == DataScopeLevel.CLASS and class_id not in scope.scope_ids:
        return {
            "classId": class_id,
            "degraded": True,
            "degraded_reason": "datascope_violation",
            "averageScore": 0.0,
            "passRate": 0.0,
            "totalStudents": 0,
        }

    result = await clickhouse_repository.query_class_performance(
        class_id=class_id,
        subject_id=subject_id,
        start_date=start_date,
        end_date=end_date,
    )
    if result is None:
        return {
            "classId": class_id,
            "degraded": True,
            "degraded_reason": "clickhouse_unavailable",
            "averageScore": 0.0,
            "passRate": 0.0,
            "totalStudents": 0,
        }

    if degraded:
        result["degraded"] = True
        result["degraded_reason"] = scope.degraded_reason or "iam_grpc_unavailable"
    return result


async def get_mastery_distribution(
    user: UserContext,
    class_id: str,
    subject_id: str = "",
    knowledge_point_id: str = "",
) -> dict[str, Any]:
    """查询班级掌握度分布（GetMasteryDistribution RPC 实现）."""
    scope = await iam_client.get_effective_datascope(user)
    degraded = scope.degraded

    # DataScope 校验
    if scope.level == DataScopeLevel.CLASS and class_id not in scope.scope_ids:
        return {
            "classId": class_id,
            "degraded": True,
            "degraded_reason": "datascope_violation",
            "masteredCount": 0,
            "progressingCount": 0,
            "weakCount": 0,
            "totalStudents": 0,
        }

    result = await clickhouse_repository.query_mastery_distribution(
        class_id=class_id,
        subject_id=subject_id,
        knowledge_point_id=knowledge_point_id,
    )
    if result is None:
        return {
            "classId": class_id,
            "degraded": True,
            "degraded_reason": "clickhouse_unavailable",
            "masteredCount": 0,
            "progressingCount": 0,
            "weakCount": 0,
            "totalStudents": 0,
        }

    if degraded:
        result["degraded"] = True
        result["degraded_reason"] = scope.degraded_reason or "iam_grpc_unavailable"
    return result


async def get_student_mastery(
    user: UserContext,
    student_id: str,
    subject_id: str = "",
) -> dict[str, Any]:
    """查询学生知识点掌握度明细（GetStudentMastery RPC 实现）."""
    scope = await iam_client.get_effective_datascope(user)
    degraded = scope.degraded

    # SELF 级强制限制
    effective_student_id = student_id
    if scope.level == DataScopeLevel.SELF:
        effective_student_id = user.user_id

    result = await clickhouse_repository.query_mastery_snapshot(
        student_id=effective_student_id,
        subject_id=subject_id,
    )
    if result is None:
        return {
            "studentId": effective_student_id,
            "degraded": True,
            "degraded_reason": "clickhouse_unavailable",
            "knowledgePoints": [],
            "overallMastery": 0.0,
        }

    if degraded:
        result["degraded"] = True
        result["degraded_reason"] = scope.degraded_reason or "iam_grpc_unavailable"
    return result


async def get_student_weakness(
    user: UserContext,
    student_id: str,
    subject_id: str = "",
) -> dict[str, Any]:
    """查询学生薄弱知识点（GetStudentWeakness RPC 实现）."""
    scope = await iam_client.get_effective_datascope(user)
    degraded = scope.degraded

    # SELF 级强制限制
    effective_student_id = student_id
    if scope.level == DataScopeLevel.SELF:
        effective_student_id = user.user_id

    result = await clickhouse_repository.query_student_weakness(
        student_id=effective_student_id,
        subject_id=subject_id,
    )
    if result is None:
        return {
            "studentId": effective_student_id,
            "degraded": True,
            "degraded_reason": "clickhouse_unavailable",
            "weakPoints": [],
        }

    if degraded:
        result["degraded"] = True
        result["degraded_reason"] = scope.degraded_reason or "iam_grpc_unavailable"
    return result


async def get_learning_trend(
    user: UserContext,
    student_id: str,
    subject_id: str = "",
    start_date: int = 0,
    end_date: int = 0,
) -> dict[str, Any]:
    """查询学习趋势（GetLearningTrend RPC 实现）."""
    scope = await iam_client.get_effective_datascope(user)
    degraded = scope.degraded

    # SELF 级强制限制
    effective_student_id = student_id
    if scope.level == DataScopeLevel.SELF:
        effective_student_id = user.user_id

    result = await clickhouse_repository.query_learning_trend(
        student_id=effective_student_id,
        subject_id=subject_id,
        start_date=start_date,
        end_date=end_date,
    )
    if result is None:
        return {
            "studentId": effective_student_id,
            "degraded": True,
            "degraded_reason": "clickhouse_unavailable",
            "points": [],
        }

    if degraded:
        result["degraded"] = True
        result["degraded_reason"] = scope.degraded_reason or "iam_grpc_unavailable"
    return result
