import { describe, it, expect } from "vitest"
import type { StudentHomeworkAssignmentListItem } from "@/modules/homework/types"
import {
  computeStudentStats,
  sortUpcomingAssignments,
  filterTodaySchedule,
  toWeekday,
  buildStudentOverviewData,
} from "./student-overview-data"

// 测试辅助：构造最小合法 StudentHomeworkAssignmentListItem
function makeAssignment(
  over: Partial<StudentHomeworkAssignmentListItem> & Pick<StudentHomeworkAssignmentListItem, "id" | "dueAt" | "progressStatus">
): StudentHomeworkAssignmentListItem {
  return {
    title: "Test Assignment",
    subjectName: null,
    availableAt: null,
    maxAttempts: 1,
    attemptsUsed: 0,
    latestSubmissionId: null,
    latestSubmittedAt: null,
    latestScore: null,
    ...over,
  }
}

describe("student-overview-data", () => {
  describe("toWeekday", () => {
    it("should return 1 for Monday", () => {
      const monday = new Date("2024-01-01T10:00:00Z") // Monday
      expect(toWeekday(monday)).toBe(1)
    })

    it("should return 7 for Sunday", () => {
      const sunday = new Date("2024-01-07T10:00:00Z") // Sunday
      expect(toWeekday(sunday)).toBe(7)
    })

    it("should return 4 for Thursday", () => {
      const thursday = new Date("2024-01-04T10:00:00Z") // Thursday
      expect(toWeekday(thursday)).toBe(4)
    })
  })

  describe("computeStudentStats", () => {
    const now = new Date("2024-06-15T10:00:00Z")

    it("should return all zeros for empty assignments", () => {
      const stats = computeStudentStats([], now)
      expect(stats).toEqual({ dueSoonCount: 0, overdueCount: 0, gradedCount: 0 })
    })

    it("should count graded assignments", () => {
      const assignments = [
        { id: "1", dueAt: null, progressStatus: "graded" },
        { id: "2", dueAt: null, progressStatus: "not_started" },
        { id: "3", dueAt: null, progressStatus: "graded" },
      ]
      const stats = computeStudentStats(assignments, now)
      expect(stats.gradedCount).toBe(2)
    })

    it("should count overdue assignments (not graded, due before now)", () => {
      const pastDate = new Date("2024-06-10T10:00:00Z").toISOString()
      const assignments = [
        { id: "1", dueAt: pastDate, progressStatus: "not_started" },
        { id: "2", dueAt: pastDate, progressStatus: "graded" }, // graded → not overdue
        { id: "3", dueAt: null, progressStatus: "not_started" }, // no due date → not overdue
      ]
      const stats = computeStudentStats(assignments, now)
      expect(stats.overdueCount).toBe(1)
    })

    it("should count due-soon assignments (due within 7 days, not graded)", () => {
      const in3Days = new Date("2024-06-18T10:00:00Z").toISOString()
      const in10Days = new Date("2024-06-25T10:00:00Z").toISOString()
      const assignments = [
        { id: "1", dueAt: in3Days, progressStatus: "not_started" }, // due soon
        { id: "2", dueAt: in10Days, progressStatus: "not_started" }, // too far
        { id: "3", dueAt: in3Days, progressStatus: "graded" }, // graded → not due soon
      ]
      const stats = computeStudentStats(assignments, now)
      expect(stats.dueSoonCount).toBe(1)
    })
  })

  describe("sortUpcomingAssignments", () => {
    it("should sort by dueAt ascending (nulls last)", () => {
      const assignments = [
        { id: "c", dueAt: null, progressStatus: "not_started" },
        { id: "a", dueAt: "2024-06-20T10:00:00Z", progressStatus: "not_started" },
        { id: "b", dueAt: "2024-06-18T10:00:00Z", progressStatus: "not_started" },
      ]
      const sorted = sortUpcomingAssignments(assignments)
      expect(sorted[0].id).toBe("b")
      expect(sorted[1].id).toBe("a")
      expect(sorted[2].id).toBe("c")
    })

    it("should respect limit parameter", () => {
      const assignments = Array.from({ length: 10 }, (_, i) => ({
        id: String(i),
        dueAt: new Date(2024, 5, 20 + i).toISOString(),
        progressStatus: "not_started",
      }))
      const sorted = sortUpcomingAssignments(assignments, 3)
      expect(sorted).toHaveLength(3)
      expect(sorted[0].id).toBe("0")
      expect(sorted[2].id).toBe("2")
    })

    it("should not mutate original array", () => {
      const assignments = [
        { id: "b", dueAt: "2024-06-18T10:00:00Z", progressStatus: "not_started" },
        { id: "a", dueAt: "2024-06-20T10:00:00Z", progressStatus: "not_started" },
      ]
      const original = [...assignments]
      sortUpcomingAssignments(assignments)
      expect(assignments.map((a) => a.id)).toEqual(original.map((a) => a.id))
    })
  })

  describe("filterTodaySchedule", () => {
    it("should filter items matching today's weekday", () => {
      // 2024-06-17 is Monday (weekday 1)
      const monday = new Date("2024-06-17T10:00:00Z")
      const schedule = [
        { id: "1", classId: "c1", className: "Class 1", course: "Math", startTime: "08:00", endTime: "09:00", location: "Room 1", weekday: 1 },
        { id: "2", classId: "c2", className: "Class 2", course: "English", startTime: "10:00", endTime: "11:00", location: null, weekday: 2 },
        { id: "3", classId: "c3", className: "Class 3", course: "Science", startTime: "14:00", endTime: "15:00", location: "Lab", weekday: 1 },
      ]
      const result = filterTodaySchedule(schedule, monday)
      expect(result).toHaveLength(2)
      expect(result[0].id).toBe("1")
      expect(result[1].id).toBe("3")
      expect(result[0].location).toBe("Room 1")
      expect(result[1].location).toBe("Lab")
    })

    it("should return empty array when no items match today's weekday", () => {
      const monday = new Date("2024-06-17T10:00:00Z")
      const schedule = [
        { id: "1", classId: "c1", className: "Class 1", course: "Math", startTime: "08:00", endTime: "09:00", location: null, weekday: 2 },
      ]
      const result = filterTodaySchedule(schedule, monday)
      expect(result).toEqual([])
    })
  })

  describe("buildStudentOverviewData", () => {
    it("should assemble complete overview data", () => {
      const now = new Date("2024-06-17T10:00:00Z") // Monday
      const classes = [{ id: "1" }, { id: "2" }]
      const schedule = [
        { id: "s1", classId: "c1", className: "Class 1", course: "Math", startTime: "08:00", endTime: "09:00", location: "Room 1", weekday: 1 },
      ]
      const assignments = [
        makeAssignment({ id: "a1", dueAt: "2024-06-18T10:00:00Z", progressStatus: "not_started" }),
      ]
      const grades = { math: 90 }

      const result = buildStudentOverviewData({
        classes,
        schedule,
        assignments,
        grades,
        now,
      })

      expect(result.enrolledClassCount).toBe(2)
      expect(result.dueSoonCount).toBe(1)
      expect(result.overdueCount).toBe(0)
      expect(result.gradedCount).toBe(0)
      expect(result.todayScheduleItems).toHaveLength(1)
      expect(result.upcomingAssignments).toHaveLength(1)
      expect(result.grades).toEqual({ math: 90 })
    })
  })
})
