import { Body, Controller, Get, Param, Post, Put, Req } from "@nestjs/common";
import { z } from "zod";
import {
  GradesService,
  type RecordGradeInput,
  type UpdateGradeInput,
} from "./grades.service.js";
import {
  Permissions,
  RequirePermission,
} from "../middleware/permission.guard.js";
import type { AuthenticatedRequest } from "../middleware/auth.middleware.js";
import {
  UnauthorizedError,
  ValidationError,
} from "../shared/errors/application-error.js";

const recordGradeSchema = z
  .object({
    studentId: z.string().min(1),
    examId: z.string().optional(),
    homeworkId: z.string().optional(),
    score: z.string().min(1),
    totalScore: z.string().min(1),
    feedback: z.string().optional(),
    schoolId: z.string().min(1),
    idempotencyKey: z.string().optional(),
  })
  .refine((data) => data.examId || data.homeworkId, {
    message: "Either examId or homeworkId must be provided",
  });

const updateGradeSchema = z.object({
  score: z.string().min(1).optional(),
  feedback: z.string().optional(),
});

@Controller("v1/grades")
export class GradesController {
  constructor(private readonly gradesService: GradesService) {}

  @Post()
  @RequirePermission(Permissions.GRADE_CREATE)
  async record(
    @Body() body: unknown,
    @Req() req: AuthenticatedRequest,
  ): Promise<{ success: true; data: { id: string } }> {
    const userId = req.userId;
    if (!userId) {
      throw new UnauthorizedError("Missing x-user-id header");
    }
    const parsed = recordGradeSchema.safeParse(body);
    if (!parsed.success) {
      throw new ValidationError("Invalid grade input", parsed.error.flatten());
    }
    const input: RecordGradeInput = {
      ...parsed.data,
      gradedBy: userId,
    };
    const result = await this.gradesService.recordGrade(input, userId);
    return { success: true, data: result };
  }

  @Get(":id")
  @RequirePermission(Permissions.GRADE_READ)
  async findOne(@Param("id") id: string): Promise<{
    success: true;
    data: Awaited<ReturnType<GradesService["getGrade"]>>;
  }> {
    const data = await this.gradesService.getGrade(id);
    return { success: true, data };
  }

  @Get("student/:studentId")
  @RequirePermission(Permissions.GRADE_READ)
  async listByStudent(@Param("studentId") studentId: string): Promise<{
    success: true;
    data: Awaited<ReturnType<GradesService["listByStudent"]>>;
  }> {
    const data = await this.gradesService.listByStudent(studentId);
    return { success: true, data };
  }

  @Get("exam/:examId")
  @RequirePermission(Permissions.GRADE_READ)
  async listByExam(@Param("examId") examId: string): Promise<{
    success: true;
    data: Awaited<ReturnType<GradesService["listByExam"]>>;
  }> {
    const data = await this.gradesService.listByExam(examId);
    return { success: true, data };
  }

  @Get("homework/:homeworkId")
  @RequirePermission(Permissions.GRADE_READ)
  async listByHomework(@Param("homeworkId") homeworkId: string): Promise<{
    success: true;
    data: Awaited<ReturnType<GradesService["listByHomework"]>>;
  }> {
    const data = await this.gradesService.listByHomework(homeworkId);
    return { success: true, data };
  }

  @Put(":id")
  @RequirePermission(Permissions.GRADE_UPDATE)
  async update(
    @Param("id") id: string,
    @Body() body: unknown,
    @Req() req: AuthenticatedRequest,
  ): Promise<{ success: true; data: { success: true } }> {
    const userId = req.userId;
    if (!userId) {
      throw new UnauthorizedError("Missing x-user-id header");
    }
    const parsed = updateGradeSchema.safeParse(body);
    if (!parsed.success) {
      throw new ValidationError(
        "Invalid grade update input",
        parsed.error.flatten(),
      );
    }
    const data: UpdateGradeInput = parsed.data;
    await this.gradesService.updateGrade(id, data, userId);
    return { success: true, data: { success: true } };
  }
}
