import {
  ProgressReportDeadlineSlot,
  ProgressReportMeetingSlot,
} from '@/generated/prisma/client';
import type { UploadedFileMetadata } from '@/modules/upload/upload.service';

import type {
  ProgressReportDeadlineRecord,
  ProgressReportListMeetingRecord,
} from './progress-reports.repository';

// Shapes the API returns. Deadlines and meetings are keyed by slot so the
// frontend never has to guess meaning from array order.
export type DeadlineResponse = {
  id: number;
  slot: ProgressReportDeadlineSlot;
  date: string;
  status: ProgressReportDeadlineRecord['status'];
};

export type DeadlinesResponse = {
  firstDeadline: DeadlineResponse | null;
  secondDeadline: DeadlineResponse | null;
  finalDeadline: DeadlineResponse | null;
};

export type ListMeetingResponse = {
  id: number;
  slot: ProgressReportMeetingSlot;
  title: string;
  meetingDate: string;
  startTime: string;
  endTime: string;
  status: ProgressReportListMeetingRecord['status'];
};

export type MeetingsResponse<TMeeting> = {
  firstMeeting: TMeeting | null;
  secondMeeting: TMeeting | null;
};

// A stored date-only column is read back at UTC midnight, so slicing the ISO
// string keeps the calendar day the user picked.
export function toDateOnly(value: Date): string {
  return value.toISOString().slice(0, 10);
}

export function toTimeOnly(value: Date): string {
  return value.toISOString().slice(11, 19);
}

export function mapDeadline(
  deadline: ProgressReportDeadlineRecord,
): DeadlineResponse {
  return {
    id: deadline.id,
    slot: deadline.slot,
    date: toDateOnly(deadline.deadline),
    status: deadline.status,
  };
}

// Always returns all three keys, using null for a slot that has no deadline.
export function mapDeadlines(
  deadlines: readonly ProgressReportDeadlineRecord[],
): DeadlinesResponse {
  return {
    firstDeadline: findDeadline(deadlines, ProgressReportDeadlineSlot.FIRST),
    secondDeadline: findDeadline(deadlines, ProgressReportDeadlineSlot.SECOND),
    finalDeadline: findDeadline(deadlines, ProgressReportDeadlineSlot.FINAL),
  };
}

export function mapListMeeting(
  meeting: ProgressReportListMeetingRecord,
): ListMeetingResponse {
  return {
    id: meeting.id,
    slot: meeting.slot,
    title: meeting.title,
    meetingDate: toDateOnly(meeting.meetingDate),
    startTime: toTimeOnly(meeting.startTime),
    endTime: toTimeOnly(meeting.endTime),
    status: meeting.status,
  };
}

// Always returns both keys, using null for a slot that has no meeting.
export function mapListMeetings(
  meetings: readonly ProgressReportListMeetingRecord[],
): MeetingsResponse<ListMeetingResponse> {
  return {
    firstMeeting: findMeeting(meetings, ProgressReportMeetingSlot.FIRST),
    secondMeeting: findMeeting(meetings, ProgressReportMeetingSlot.SECOND),
  };
}

// The file columns store a path. Responses expose the metadata object the
// upload module builds from it.
export type ProgressReportFileFields = {
  attachment: UploadedFileMetadata | null;
  draftSemesterReport: UploadedFileMetadata | null;
  finalSemesterReport: UploadedFileMetadata | null;
};

function findDeadline(
  deadlines: readonly ProgressReportDeadlineRecord[],
  slot: ProgressReportDeadlineSlot,
): DeadlineResponse | null {
  const match = deadlines.find((deadline) => deadline.slot === slot);

  return match ? mapDeadline(match) : null;
}

function findMeeting(
  meetings: readonly ProgressReportListMeetingRecord[],
  slot: ProgressReportMeetingSlot,
): ListMeetingResponse | null {
  const match = meetings.find((meeting) => meeting.slot === slot);

  return match ? mapListMeeting(match) : null;
}
