using Jiaowu.Api.Domain.Academic;
using Jiaowu.Api.Domain.Identity;
using Jiaowu.Api.Infrastructure.Auth;
using Jiaowu.Api.Infrastructure.Persistence;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace Jiaowu.Api.Controllers;

[ApiController]
[Authorize]
[Route("api/dashboard")]
public sealed class DashboardController(
    AppDbContext db,
    ICurrentUserDataScope currentUserDataScope) : ControllerBase
{
    [HttpGet]
    public async Task<ActionResult<DashboardResponse>> Get(
        CancellationToken cancellationToken)
    {
        var scope = currentUserDataScope.Current;
        Guid? restrictedCollegeId = scope.Scope == DataScope.All
            ? null
            : scope.CollegeId ?? Guid.Empty;
        var collegeName = restrictedCollegeId.HasValue &&
                          restrictedCollegeId.Value != Guid.Empty
            ? await db.Colleges.AsNoTracking()
                .Where(x => x.Id == restrictedCollegeId.Value)
                .Select(x => x.Name)
                .FirstOrDefaultAsync(cancellationToken)
            : null;

        var currentTerm = await db.AcademicTerms
            .AsNoTracking()
            .Where(x => x.IsCurrent)
            .Select(x => new DashboardTerm(
                x.Id,
                x.Name,
                x.StartDate,
                x.EndDate))
            .FirstOrDefaultAsync(cancellationToken);
        var currentTermId = currentTerm?.Id;

        var students = db.Students.AsNoTracking()
            .Where(x =>
                !restrictedCollegeId.HasValue ||
                x.AdministrativeClass!.Major!.CollegeId ==
                restrictedCollegeId.Value);
        var teachers = db.Teachers.AsNoTracking()
            .Where(x =>
                !restrictedCollegeId.HasValue ||
                x.CollegeId == restrictedCollegeId.Value);
        var courses = db.Courses.AsNoTracking()
            .Where(x =>
                !restrictedCollegeId.HasValue ||
                x.CollegeId == restrictedCollegeId.Value);
        var teachingTasks = db.TeachingTasks.AsNoTracking()
            .Where(x =>
                currentTermId.HasValue &&
                x.AcademicTermId == currentTermId.Value &&
                (!restrictedCollegeId.HasValue ||
                 x.Course!.CollegeId == restrictedCollegeId.Value));
        var gradeSheets = db.GradeSheets.AsNoTracking()
            .Where(x =>
                currentTermId.HasValue &&
                x.TeachingTask!.AcademicTermId == currentTermId.Value &&
                (!restrictedCollegeId.HasValue ||
                 x.TeachingTask.Course!.CollegeId ==
                 restrictedCollegeId.Value));
        var enrollments = db.CourseEnrollments.AsNoTracking()
            .Where(x =>
                currentTermId.HasValue &&
                x.Status == CourseEnrollmentStatus.Enrolled &&
                x.CourseSelectionOffering!.CourseSelectionRound!
                    .AcademicTermId == currentTermId.Value &&
                (!restrictedCollegeId.HasValue ||
                 x.CourseSelectionOffering.TeachingTask!.Course!.CollegeId ==
                 restrictedCollegeId.Value));

        var taskCount = await teachingTasks.CountAsync(cancellationToken);
        var publishedTaskCount = await teachingTasks.CountAsync(
            x => x.Status == TeachingTaskStatus.Published,
            cancellationToken);
        var scheduledTaskCount = currentTermId.HasValue
            ? await db.ScheduleEntries.AsNoTracking()
                .Where(x =>
                    x.SchedulePlan!.AcademicTermId == currentTermId.Value &&
                    x.SchedulePlan.Status == SchedulePlanStatus.Published &&
                    (!restrictedCollegeId.HasValue ||
                     x.TeachingTask!.Course!.CollegeId ==
                     restrictedCollegeId.Value))
                .Select(x => x.TeachingTaskId)
                .Distinct()
                .CountAsync(cancellationToken)
            : 0;
        var gradeSheetCount = await gradeSheets.CountAsync(cancellationToken);
        var publishedGradeSheetCount = await gradeSheets.CountAsync(
            x => x.Status == GradeSheetStatus.Published,
            cancellationToken);

        var counts = new DashboardCounts(
            await students.CountAsync(
                x => x.Status == StudentStatus.Active,
                cancellationToken),
            await teachers.CountAsync(
                x => x.Status == TeacherStatus.Active,
                cancellationToken),
            await courses.CountAsync(
                x => x.IsEnabled,
                cancellationToken),
            taskCount,
            publishedTaskCount,
            scheduledTaskCount,
            await enrollments.CountAsync(cancellationToken),
            gradeSheetCount,
            publishedGradeSheetCount,
            await gradeSheets.CountAsync(
                x => x.Status == GradeSheetStatus.Submitted,
                cancellationToken),
            currentTermId.HasValue
                ? await db.CourseSelectionRounds.AsNoTracking().CountAsync(
                    x => x.AcademicTermId == currentTermId.Value &&
                         x.Status == CourseSelectionRoundStatus.Open,
                    cancellationToken)
                : 0);

        var pending = await LoadPendingAsync(
            scope,
            restrictedCollegeId,
            cancellationToken);

        return Ok(new DashboardResponse(
            BuildAudience(scope, collegeName),
            currentTerm,
            counts,
            pending,
            await BuildGreetingAsync(scope, currentTermId, counts, pending, cancellationToken),
            DateTime.UtcNow));
    }

    [HttpGet("greeting")]
    public async Task<ActionResult<DashboardGreeting>> GetGreeting(
        CancellationToken cancellationToken)
    {
        var currentTermId = await db.AcademicTerms.AsNoTracking()
            .Where(x => x.IsCurrent)
            .Select(x => (Guid?)x.Id)
            .FirstOrDefaultAsync(cancellationToken);

        return Ok(await BuildGreetingAsync(
            currentUserDataScope.Current,
            currentTermId,
            null,
            null,
            cancellationToken));
    }

    private async Task<DashboardGreeting> BuildGreetingAsync(
        CurrentUserScope scope,
        Guid? currentTermId,
        DashboardCounts? counts,
        DashboardPending? pending,
        CancellationToken cancellationToken)
    {
        var name = string.IsNullOrWhiteSpace(scope.DisplayName) ? "" : $"{scope.DisplayName}，";
        var greeting = GetTimeGreeting();
        var isManager = scope.IsInRole(SystemRoles.SuperAdmin) ||
                        scope.IsInRole(SystemRoles.AcademicAdmin) ||
                        scope.IsInRole(SystemRoles.CollegeAdmin) ||
                        scope.IsInRole(SystemRoles.Leader) ||
                        scope.IsInRole(SystemRoles.Counselor);

        if (!isManager && scope.IsInRole(SystemRoles.Student))
        {
            var studentId = await db.Students.AsNoTracking()
                .Where(x => x.UserId == scope.UserId)
                .Select(x => (Guid?)x.Id)
                .FirstOrDefaultAsync(cancellationToken);
            if (!studentId.HasValue)
                return new DashboardGreeting("Student", $"{name}{greeting}", "绑定学籍后，将为你生成课程与成绩学习概览。", "学习节奏", "关联学生档案后，可从课程安排、成绩和考试中生成学习状态摘要。", []);

            var student = await db.Students.AsNoTracking()
                .Where(x => x.Id == studentId.Value)
                .Select(x => new { x.AdministrativeClassId })
                .FirstAsync(cancellationToken);
            var currentTasks = db.TeachingTasks.AsNoTracking().Where(task =>
                currentTermId.HasValue &&
                task.AcademicTermId == currentTermId.Value &&
                task.Status == TeachingTaskStatus.Published &&
                (task.Classes.Any(item =>
                     item.AdministrativeClassId == student.AdministrativeClassId) ||
                 db.CourseEnrollments.Any(enrollment =>
                     enrollment.StudentId == studentId.Value &&
                     enrollment.Status == CourseEnrollmentStatus.Enrolled &&
                     enrollment.CourseSelectionOffering!.TeachingTaskId == task.Id)));
            var taskWorkload = await currentTasks.Select(task => new
            {
                task.Id,
                task.CourseId,
                Credits = task.Course!.Credits,
                IsClassAssigned = task.Classes.Any(item =>
                    item.AdministrativeClassId == student.AdministrativeClassId)
            }).ToListAsync(cancellationToken);
            var currentCourses = taskWorkload
                .GroupBy(x => x.CourseId)
                .Select(x => x.First())
                .ToList();
            var courseCount = currentCourses.Count;
            var courseCredits = currentCourses.Sum(x => x.Credits);
            var classAssignedCount = taskWorkload.Count(x => x.IsClassAssigned);
            var selfSelectedCount = taskWorkload.Count(x => !x.IsClassAssigned);
            var publishedGrades = db.GradeRecords.AsNoTracking().Where(x =>
                x.StudentId == studentId.Value &&
                x.GradeSheet!.Status == GradeSheetStatus.Published &&
                currentTermId.HasValue &&
                x.GradeSheet.TeachingTask!.AcademicTermId == currentTermId.Value);
            var gradeCount = await publishedGrades.CountAsync(cancellationToken);
            var average = await publishedGrades
                .Where(x => x.ExamStatus == GradeExamStatus.Normal && x.TotalScore.HasValue)
                .AverageAsync(x => (decimal?)x.TotalScore, cancellationToken);
            var failed = await publishedGrades.CountAsync(x =>
                x.ExamStatus == GradeExamStatus.Normal && x.TotalScore.HasValue && x.TotalScore < 60,
                cancellationToken);

            var subtitle = failed > 0
                ? $"已发布成绩中有 {failed} 门课程需要重点关注，建议优先查看课程反馈。"
                : courseCount > 0
                    ? $"本学期已有 {courseCount} 门课程、{courseCredits:0.#} 学分进入你的学习安排。"
                    : "本学期暂未发现为你安排或确认选课的课程，可先查看培养方案和选课安排。";
            var narrative = courseCount == 0
                ? "你的当前学习安排尚未形成：系统还没有找到行政班已安排课程或已确认选课。"
                : failed > 0
                    ? $"本学期已形成 {courseCount} 门课程安排，其中 {classAssignedCount} 个教学班来自行政班安排；已发布成绩中有 {failed} 门需要重点关注。"
                    : gradeCount > 0
                        ? $"本学期有 {courseCount} 门课程进入学习安排，已发布 {gradeCount} 门成绩，当前没有不及格记录。"
                        : $"本学期有 {courseCount} 门课程进入学习安排，包含 {classAssignedCount} 个行政班教学班和 {selfSelectedCount} 个自主选课教学班，成绩发布后会在这里更新。";
            return new DashboardGreeting("Student", $"{name}{greeting}", subtitle, "学习节奏", narrative,
            [
                new DashboardGreetingInsight("本学期课程", $"{courseCount} 门", $"共 {courseCredits:0.#} 学分", "calm"),
                new DashboardGreetingInsight("已发布成绩", $"{gradeCount} 门", average.HasValue ? $"平均分 {average.Value:0.0}" : "等待成绩发布", "calm"),
                new DashboardGreetingInsight("重点关注", $"{failed} 门", failed > 0 ? "建议尽早安排复习与答疑" : "当前无不及格记录", failed > 0 ? "attention" : "positive")
            ]);
        }

        if (!isManager && scope.IsInRole(SystemRoles.Teacher))
        {
            var teacherId = await db.Teachers.AsNoTracking()
                .Where(x => x.UserId == scope.UserId && x.Status == TeacherStatus.Active)
                .Select(x => (Guid?)x.Id)
                .FirstOrDefaultAsync(cancellationToken);
            if (!teacherId.HasValue)
                return new DashboardGreeting("Teacher", $"{name}{greeting}", "绑定教师档案后，将为你生成本学期教学负荷概览。", "教学节奏", "关联教师档案后，可从教学班、授课学时和成绩进度生成今日工作摘要。", []);

            var tasks = db.TeachingTasks.AsNoTracking().Where(x =>
                currentTermId.HasValue && x.AcademicTermId == currentTermId.Value &&
                x.Teachers.Any(t => t.TeacherId == teacherId.Value));
            var teachingClasses = await tasks.CountAsync(cancellationToken);
            var estimatedHours = await tasks.SumAsync(
                x => (int?)(x.WeeklyHours * (x.EndWeek - x.StartWeek + 1)), cancellationToken) ?? 0;
            var gradeSheets = db.GradeSheets.AsNoTracking().Where(x =>
                x.TeachingTask!.Teachers.Any(t => t.TeacherId == teacherId.Value) &&
                currentTermId.HasValue && x.TeachingTask.AcademicTermId == currentTermId.Value);
            var pendingGrades = await gradeSheets.CountAsync(x =>
                x.Status == GradeSheetStatus.Draft ||
                x.Status == GradeSheetStatus.Returned, cancellationToken);
            var submittedGrades = await gradeSheets.CountAsync(x => x.Status == GradeSheetStatus.Submitted, cancellationToken);
            var subtitle = pendingGrades > 0
                ? $"有 {pendingGrades} 张成绩登记册尚待提交，完成后可进入审核流程。"
                : teachingClasses > 0
                    ? $"本学期承担 {teachingClasses} 个教学班，预计授课 {estimatedHours} 学时。"
                    : "本学期暂未分配教学班，请留意教学任务安排。";
            var narrative = pendingGrades > 0
                ? $"你本学期承担 {teachingClasses} 个教学班，预计授课 {estimatedHours} 学时；有 {pendingGrades} 张成绩登记册等待提交。"
                : $"你本学期承担 {teachingClasses} 个教学班，预计授课 {estimatedHours} 学时，目前没有待提交的成绩登记册。";
            return new DashboardGreeting("Teacher", $"{name}{greeting}", subtitle, "教学节奏", narrative,
            [
                new DashboardGreetingInsight("教学班", $"{teachingClasses} 个", $"预计 {estimatedHours} 学时", "calm"),
                new DashboardGreetingInsight("待提交成绩", $"{pendingGrades} 张", pendingGrades > 0 ? "请在截止日前完成登记" : "当前无需提交", pendingGrades > 0 ? "attention" : "positive"),
                new DashboardGreetingInsight("审核中成绩", $"{submittedGrades} 张", submittedGrades > 0 ? "等待审核结果" : "暂无审核中登记册", "calm")
            ]);
        }

        var actionable = pending is null ? 0 : pending.TeacherApplications + pending.GradeSheets +
            pending.CourseAdjustments + pending.StudentStatusChanges + pending.GradeModifications +
            pending.ClassroomReservations + pending.GeneralApprovals;
        var taskCount = counts?.TeachingTasks ?? 0;
        var scheduledCount = counts?.ScheduledTeachingTasks ?? 0;
        var subtitleForManager = actionable > 0
            ? $"当前有 {actionable} 项待办需要跟进，优先处理时效性审核事项。"
            : taskCount > 0
                ? $"本学期 {taskCount} 个教学班正在运行，当前没有积压待办。"
                : "当前学期运行数据已就绪，可从教学任务开始推进。";
        var managerNarrative = actionable > 0
            ? $"当前教学运行覆盖 {taskCount} 个教学班，其中 {scheduledCount} 个已进入课表；{actionable} 项待办正等待处理。"
            : $"当前教学运行覆盖 {taskCount} 个教学班，其中 {scheduledCount} 个已进入课表，暂未发现需要你处理的积压事项。";
        return new DashboardGreeting("Manager", $"{name}{greeting}", subtitleForManager, "运行态势", managerNarrative,
        [
            new DashboardGreetingInsight("当前待办", $"{actionable} 项", actionable > 0 ? "优先处理可操作事项" : "暂无积压", actionable > 0 ? "attention" : "positive"),
            new DashboardGreetingInsight("本学期教学班", $"{taskCount} 个", "教学运行规模", "calm"),
            new DashboardGreetingInsight("已进入课表", $"{scheduledCount} 个", taskCount > 0 ? $"覆盖 {Math.Round(scheduledCount * 100d / taskCount)}% 教学班" : "等待教学任务发布", "calm")
        ]);
    }

    private static string GetTimeGreeting()
    {
        var hour = DateTime.UtcNow.AddHours(8).Hour;
        return hour < 11 ? "早上好" : hour < 14 ? "中午好" : hour < 18 ? "下午好" : "晚上好";
    }

    private async Task<DashboardPending> LoadPendingAsync(
        CurrentUserScope scope,
        Guid? restrictedCollegeId,
        CancellationToken cancellationToken)
    {
        var isSchoolManager =
            scope.IsInRole(SystemRoles.SuperAdmin) ||
            scope.IsInRole(SystemRoles.AcademicAdmin);
        var isCollegeManager =
            !isSchoolManager && scope.IsInRole(SystemRoles.CollegeAdmin);
        if (!isSchoolManager && !isCollegeManager)
            return new DashboardPending(0, 0, 0, 0, 0, 0, 0);

        var teacherApplications = db.TeacherCourseApplications.AsNoTracking()
            .Where(x =>
                x.Status == TeacherCourseApplicationStatus.Pending &&
                (!restrictedCollegeId.HasValue ||
                 x.Teacher!.CollegeId == restrictedCollegeId.Value));
        var gradeSheets = db.GradeSheets.AsNoTracking()
            .Where(x =>
                x.Status == GradeSheetStatus.Submitted &&
                (!restrictedCollegeId.HasValue ||
                 x.TeachingTask!.Course!.CollegeId ==
                 restrictedCollegeId.Value));
        var courseAdjustments = db.CourseAdjustments.AsNoTracking()
            .Where(x =>
                x.Status == CourseAdjustmentStatus.Submitted &&
                (!restrictedCollegeId.HasValue ||
                 x.TeachingTask!.Course!.CollegeId ==
                 restrictedCollegeId.Value));
        var studentStatusChanges = db.StudentStatusChanges.AsNoTracking()
            .Where(x =>
                x.State == (isCollegeManager
                    ? StudentStatusChangeState.CounselorApproved
                    : StudentStatusChangeState.CollegeApproved) &&
                (!restrictedCollegeId.HasValue ||
                 x.Student!.AdministrativeClass!.Major!.CollegeId ==
                 restrictedCollegeId.Value));
        var gradeModifications = db.GradeModifications.AsNoTracking()
            .Where(x =>
                x.Status == (isCollegeManager
                    ? GradeModificationStatus.TeacherSubmitted
                    : GradeModificationStatus.CollegeApproved) &&
                (!restrictedCollegeId.HasValue ||
                 x.GradeRecord!.GradeSheet!.TeachingTask!.Course!.CollegeId ==
                 restrictedCollegeId.Value));

        var generalApprovals =
            await db.CourseExemptions.AsNoTracking().CountAsync(
                x => x.Status == ApprovalStatus.Submitted &&
                     (!restrictedCollegeId.HasValue ||
                      x.TeachingTask!.Course!.CollegeId ==
                      restrictedCollegeId.Value),
                cancellationToken) +
            await db.DeferredExams.AsNoTracking().CountAsync(
                x => x.Status == ApprovalStatus.Submitted &&
                     (!restrictedCollegeId.HasValue ||
                      x.TeachingTask!.Course!.CollegeId ==
                      restrictedCollegeId.Value),
                cancellationToken) +
            await db.CourseSubstitutions.AsNoTracking().CountAsync(
                x => x.Status == ApprovalStatus.Submitted &&
                     (!restrictedCollegeId.HasValue ||
                      x.Student!.AdministrativeClass!.Major!.CollegeId ==
                      restrictedCollegeId.Value),
                cancellationToken) +
            await db.AttendanceRecords.AsNoTracking().CountAsync(
                x => x.AppealStatus == AttendanceAppealStatus.Pending &&
                     (!restrictedCollegeId.HasValue ||
                      x.Student!.AdministrativeClass!.Major!.CollegeId ==
                      restrictedCollegeId.Value),
                cancellationToken);

        var classroomReservations = isCollegeManager &&
                                    restrictedCollegeId.HasValue
            ? await db.ClassroomReservations.AsNoTracking().CountAsync(
                x => x.Status == ClassroomReservationStatus.Submitted &&
                     x.ApplicantCollegeId == restrictedCollegeId.Value,
                cancellationToken)
            : 0;

        return new DashboardPending(
            await teacherApplications.CountAsync(cancellationToken),
            await gradeSheets.CountAsync(cancellationToken),
            await courseAdjustments.CountAsync(cancellationToken),
            await studentStatusChanges.CountAsync(cancellationToken),
            await gradeModifications.CountAsync(cancellationToken),
            classroomReservations,
            generalApprovals);
    }

    private static DashboardAudience BuildAudience(
        CurrentUserScope scope,
        string? collegeName)
    {
        if (scope.IsInRole(SystemRoles.SuperAdmin))
            return new DashboardAudience(
                "System",
                "全域教务工作台",
                "全校",
                "统筹基础数据、教学运行与系统治理");
        if (scope.IsInRole(SystemRoles.AcademicAdmin))
            return new DashboardAudience(
                "School",
                "校级教务工作台",
                "全校",
                "聚焦跨学院教学运行与校级审核");
        if (scope.IsInRole(SystemRoles.CollegeAdmin))
            return new DashboardAudience(
                "College",
                "学院教务工作台",
                collegeName ?? "本学院",
                "聚焦本学院教学准备、过程审核与成绩归档");
        if (scope.IsInRole(SystemRoles.Leader))
            return new DashboardAudience(
                "Leadership",
                "教学运行观察台",
                "全校",
                "查看全校教学运行与质量数据");
        if (scope.IsInRole(SystemRoles.Counselor))
            return new DashboardAudience(
                "Counselor",
                "班级工作台",
                collegeName ?? "所辖班级",
                "处理学生过程管理与学业支持");
        return new DashboardAudience(
            "Teaching",
            "教学工作台",
            collegeName ?? "个人教学",
            "查看课程运行并进入日常教学工作");
    }
}

public sealed record DashboardResponse(
    DashboardAudience Audience,
    DashboardTerm? CurrentTerm,
    DashboardCounts Counts,
    DashboardPending Pending,
    DashboardGreeting Greeting,
    DateTime GeneratedAt);

public sealed record DashboardGreeting(
    string Role,
    string Title,
    string Subtitle,
    string Label,
    string Narrative,
    IReadOnlyList<DashboardGreetingInsight> Insights);

public sealed record DashboardGreetingInsight(
    string Label,
    string Value,
    string Hint,
    string Tone);

public sealed record DashboardAudience(
    string Level,
    string Title,
    string ScopeName,
    string Description);

public sealed record DashboardTerm(
    Guid Id,
    string Name,
    DateOnly StartDate,
    DateOnly EndDate);

public sealed record DashboardCounts(
    int Students,
    int Teachers,
    int Courses,
    int TeachingTasks,
    int PublishedTeachingTasks,
    int ScheduledTeachingTasks,
    int CourseEnrollments,
    int GradeSheets,
    int PublishedGradeSheets,
    int SubmittedGradeSheets,
    int OpenCourseSelectionRounds);

public sealed record DashboardPending(
    int TeacherApplications,
    int GradeSheets,
    int CourseAdjustments,
    int StudentStatusChanges,
    int GradeModifications,
    int ClassroomReservations,
    int GeneralApprovals);
