import { baseAPI } from "@/lib/api";
import type { UploadedFileMetadata } from "@/lib/document-file";
import type {
  ApiMinistryProgressReportAssignment,
  ApiProgressReportIssueComment,
  ApiProgressReportRgcDecision,
} from "@/features/ministry/progress-report/service/progress-report-service";

import {
  mapProgressReportMeetingsToCalendarEvents,
  type ProgressReportCalendarMeetingDetailSource,
} from "../progress-report-calendar-mapper";
import type { ProgressReportCalendarEvent } from "../progress-report-calendar-data";
import type { ProgressReportRow } from "../progress-report-data";

export type ProgressReportSemester = "S1" | "S2";
export type ProgressReportStatus = "DRAFT" | "SENT";
export type ProgressReportDeadlineStatus = "DRAFT" | "SENT";
// A progress report has exactly two meeting slots.
export type ProgressReportMeetingSlot = "FIRST" | "SECOND";
export type ProgressReportMinistryStatus =
  | "DRAFT"
  | "SHARED_WITH_PSWG"
  | "PSWG_REVIEWED"
  | "SUBMITTED"
  | "CDC_UNDER_REVIEW"
  | "COMPLETED";

type ProgressReportApiResponse<T> = {
  success: boolean;
  statusCode: number;
  message: string;
  data: T;
};

// The status is not part of this input: the API always creates a draft and
// only the send endpoint can move it to SENT.
export type CreateProgressReportInput = {
  title: string;
  description: string;
  year: number;
  semester: ProgressReportSemester;
  firstDeadline: string;
  attachment: File;
};

export type SendProgressReportInput = {
  progressReportId: number;
};

// The status is not part of this input: the API always creates a draft and
// only the send endpoint can move a meeting to SENT.
export type CreateProgressReportMeetingInput = {
  progressReportId: number;
  slot: ProgressReportMeetingSlot;
  title: string;
  description: string;
  meetingDate: string;
  startTime: string;
  endTime: string;
  location: string;
  document: File;
};

export type SendProgressReportMeetingInput = {
  meetingId: number;
};

export type UpdateProgressReportMeetingInput = {
  meetingId: number;
  title: string;
  description: string;
  meetingDate: string;
  startTime: string;
  endTime: string;
  location: string;
  document?: File;
};

export type UpdateProgressReportInput = {
  progressReportId: number;
  title: string;
  description: string;
  year: number;
  semester: ProgressReportSemester;
  attachment?: File;
};

// The later Set Deadline modal fills SECOND and FINAL. FIRST is created with
// the progress report itself.
export type CreateProgressReportDeadlineInput = {
  progressReportId: number;
  slot: Exclude<ProgressReportDeadlineSlot, "FIRST">;
  deadline: string;
};

export type UpdateProgressReportDeadlineInput = {
  deadlineId: number;
  deadline: string;
};

export type SendProgressReportDeadlineInput = {
  deadlineId: number;
};

export type UploadDraftSemesterInput = {
  progressReportId: number;
  file: File;
};

export type ProgressReportDeadlineSlot = "FIRST" | "SECOND" | "FINAL";

export type ApiProgressReportDeadline = {
  id: number;
  slot: ProgressReportDeadlineSlot;
  date: string;
  status: ProgressReportDeadlineStatus;
};

// The API keys deadlines and meetings by slot, so the order of a list never
// decides their meaning. A slot with no record is null.
export type ApiProgressReportDeadlines = {
  firstDeadline: ApiProgressReportDeadline | null;
  secondDeadline: ApiProgressReportDeadline | null;
  finalDeadline: ApiProgressReportDeadline | null;
};

export type ApiProgressReportMeetings = {
  firstMeeting: ApiProgressReportMeeting | null;
  secondMeeting: ApiProgressReportMeeting | null;
};

// What the report list and detail return for a meeting. The full meeting
// (description, location, document) comes from the meeting endpoints.
export type ApiProgressReportMeeting = {
  id: number;
  slot: ProgressReportMeetingSlot;
  title: string;
  meetingDate: string;
  startTime: string;
  endTime: string;
  status: ProgressReportStatus;
};

// Everything a single meeting endpoint returns.
export type ApiProgressReportMeetingDetail =
  ProgressReportCalendarMeetingDetailSource & {
    slot: ProgressReportMeetingSlot;
    progressReportId: number;
  };

export type ApiProgressReport = {
  id: number;
  title: string;
  description: unknown | null;
  year: number;
  semester: ProgressReportSemester;
  status: ProgressReportStatus;
  attachment: UploadedFileMetadata | null;
  draftSemesterReport: UploadedFileMetadata | null;
  finalSemesterReport: UploadedFileMetadata | null;
  createdAt: string;
  updatedAt: string;
  deadlines: ApiProgressReportDeadlines;
  meetings: ApiProgressReportMeetings;
};

export type ApiProgressReportMinistryAssignment = {
  id: number;
  ministryId: number;
  issues: number;
  attachment: UploadedFileMetadata | null;
  ministry: {
    id: number;
    name: string;
    description: string | null;
    logo: string | null;
  };
};

export type ProgressReportPaginationMeta = {
  page: number;
  limit: number;
  total: number;
  totalPages: number;
};

type ProgressReportListResponse = {
  success: boolean;
  statusCode: number;
  message: string;
  data: ApiProgressReport[];
  meta: ProgressReportPaginationMeta;
};

type ProgressReportMinistryListResponse = {
  success: boolean;
  statusCode: number;
  message: string;
  data: ApiProgressReportMinistryAssignment[];
  meta: ProgressReportPaginationMeta;
};

export type ApiCdcProgressReportMinistryDetail =
  ApiMinistryProgressReportAssignment & {
    rgcDecisions: ApiProgressReportRgcDecision[];
  };

export type ProgressReportListResult = {
  rows: ProgressReportRow[];
  calendarEvents: ProgressReportCalendarEvent[];
  meta: ProgressReportPaginationMeta;
};

export type ProgressReportMutationResult<T> = {
  data: T;
  message: string;
};

export type ApiCommentType = {
  id: number;
  name: string;
  description: string | null;
};

// The API keys meetings by slot; the calendar wants a plain list.
export function toMeetingList(
  meetings: ApiProgressReportMeetings,
): ApiProgressReportMeeting[] {
  return [meetings.firstMeeting, meetings.secondMeeting].filter(
    (meeting): meeting is ApiProgressReportMeeting => meeting !== null,
  );
}

export function mapProgressReportToRow(
  report: ApiProgressReport,
): ProgressReportRow {
  const description =
    report.description === null || report.description === undefined
      ? ""
      : typeof report.description === "string"
        ? report.description
        : JSON.stringify(report.description);

  return {
    id: report.id,
    title: report.title,
    description,
    semester: report.semester,
    year: String(report.year),
    deadline: report.deadlines.firstDeadline?.date || "-",
    firstDeadlineId: report.deadlines.firstDeadline?.id ?? null,
    secondDeadlineId: report.deadlines.secondDeadline?.id ?? null,
    finalDeadlineId: report.deadlines.finalDeadline?.id ?? null,
    firstDeadlineStatus: report.deadlines.firstDeadline?.status ?? null,
    secondDeadlineStatus: report.deadlines.secondDeadline?.status ?? null,
    finalDeadlineStatus: report.deadlines.finalDeadline?.status ?? null,
    firstMeeting: report.meetings.firstMeeting?.meetingDate || "-",
    firstMeetingStatus: report.meetings.firstMeeting?.status ?? null,
    secondDeadline: report.deadlines.secondDeadline?.date || "-",
    secondMeeting: report.meetings.secondMeeting?.meetingDate || "-",
    secondMeetingStatus: report.meetings.secondMeeting?.status ?? null,
    finalDeadline: report.deadlines.finalDeadline?.date || "-",
    draftSemesterDoc: report.draftSemesterReport,
    reportDoc: report.attachment,
    status: report.status === "SENT" ? "Sent" : "Draft",
  };
}

function buildProgressReportFormData(input: CreateProgressReportInput) {
  const formData = new FormData();

  formData.append("title", input.title);
  formData.append("description", input.description);
  formData.append("year", String(input.year));
  formData.append("semester", input.semester);
  formData.append("firstDeadline", input.firstDeadline);
  formData.append("attachment", input.attachment);

  return formData;
}

function buildProgressReportMeetingFormData(
  input: CreateProgressReportMeetingInput,
) {
  const formData = new FormData();

  formData.append("slot", input.slot);
  formData.append("title", input.title);
  formData.append("description", input.description);
  formData.append("meetingDate", input.meetingDate);
  formData.append("startTime", input.startTime);
  formData.append("endTime", input.endTime);
  formData.append("location", input.location);
  formData.append("document", input.document);

  return formData;
}

function buildUpdateProgressReportMeetingFormData(
  input: UpdateProgressReportMeetingInput,
) {
  const formData = new FormData();

  formData.append("title", input.title);
  formData.append("description", input.description);
  formData.append("meetingDate", input.meetingDate);
  formData.append("startTime", input.startTime);
  formData.append("endTime", input.endTime);
  formData.append("location", input.location);

  if (input.document) {
    formData.append("document", input.document);
  }

  return formData;
}

function buildUpdateProgressReportFormData(input: UpdateProgressReportInput) {
  const formData = new FormData();

  formData.append("title", input.title);
  formData.append("description", input.description);
  formData.append("year", String(input.year));
  formData.append("semester", input.semester);

  if (input.attachment) {
    formData.append("attachment", input.attachment);
  }

  return formData;
}

export async function createProgressReport(
  input: CreateProgressReportInput,
): Promise<ProgressReportMutationResult<ApiProgressReport>> {
  const response = (await baseAPI("/progress-reports", {
    method: "POST",
    body: buildProgressReportFormData(input),
  })) as ProgressReportApiResponse<ApiProgressReport>;

  return { data: response.data, message: response.message };
}

export async function createProgressReportMeeting(
  input: CreateProgressReportMeetingInput,
): Promise<ProgressReportMutationResult<ApiProgressReportMeeting>> {
  const response = (await baseAPI(
    `/progress-reports/${input.progressReportId}/meetings`,
    {
      method: "POST",
      body: buildProgressReportMeetingFormData(input),
    },
  )) as ProgressReportApiResponse<ApiProgressReportMeeting>;

  return { data: response.data, message: response.message };
}

// Sending is a separate call because the API decides the status itself.
export async function sendProgressReportMeeting(
  input: SendProgressReportMeetingInput,
): Promise<ProgressReportMutationResult<ApiProgressReportMeeting>> {
  const response = (await baseAPI(
    `/progress-report-meetings/${input.meetingId}/send`,
    { method: "PATCH" },
  )) as ProgressReportApiResponse<ApiProgressReportMeeting>;

  return { data: response.data, message: response.message };
}

export async function updateProgressReportMeeting(
  input: UpdateProgressReportMeetingInput,
): Promise<ProgressReportMutationResult<ApiProgressReportMeetingDetail>> {
  const response = (await baseAPI(
    `/progress-report-meetings/${input.meetingId}`,
    {
      method: "PATCH",
      body: buildUpdateProgressReportMeetingFormData(input),
    },
  )) as ProgressReportApiResponse<ApiProgressReportMeetingDetail>;

  return { data: response.data, message: response.message };
}

export async function updateProgressReport(
  input: UpdateProgressReportInput,
): Promise<ProgressReportMutationResult<ApiProgressReport>> {
  const response = (await baseAPI(
    `/progress-reports/${input.progressReportId}`,
    {
      method: "PATCH",
      body: buildUpdateProgressReportFormData(input),
    },
  )) as ProgressReportApiResponse<ApiProgressReport>;

  return { data: response.data, message: response.message };
}

// Sending is a separate call because the API decides the status itself.
export async function sendProgressReport(
  input: SendProgressReportInput,
): Promise<ProgressReportMutationResult<ApiProgressReport>> {
  const response = (await baseAPI(
    `/progress-reports/${input.progressReportId}/send`,
    { method: "PATCH" },
  )) as ProgressReportApiResponse<ApiProgressReport>;

  return { data: response.data, message: response.message };
}

export async function createProgressReportDeadline(
  input: CreateProgressReportDeadlineInput,
): Promise<ProgressReportMutationResult<ApiProgressReportDeadline>> {
  const response = (await baseAPI(
    `/progress-reports/${input.progressReportId}/deadlines`,
    {
      method: "POST",
      body: JSON.stringify({ slot: input.slot, deadline: input.deadline }),
    },
  )) as ProgressReportApiResponse<ApiProgressReportDeadline>;

  return { data: response.data, message: response.message };
}

export async function updateProgressReportDeadline(
  input: UpdateProgressReportDeadlineInput,
): Promise<ProgressReportMutationResult<ApiProgressReportDeadline>> {
  const response = (await baseAPI(
    `/progress-report-deadlines/${input.deadlineId}`,
    {
      method: "PATCH",
      body: JSON.stringify({ deadline: input.deadline }),
    },
  )) as ProgressReportApiResponse<ApiProgressReportDeadline>;

  return { data: response.data, message: response.message };
}

export async function sendProgressReportDeadline(
  input: SendProgressReportDeadlineInput,
): Promise<ProgressReportMutationResult<ApiProgressReportDeadline>> {
  const response = (await baseAPI(
    `/progress-report-deadlines/${input.deadlineId}/send`,
    { method: "PATCH" },
  )) as ProgressReportApiResponse<ApiProgressReportDeadline>;

  return { data: response.data, message: response.message };
}

export async function uploadDraftSemester(
  input: UploadDraftSemesterInput,
): Promise<ProgressReportMutationResult<ApiProgressReport>> {
  const formData = new FormData();
  formData.append("draftSemesterReport", input.file);

  const response = (await baseAPI(
    `/progress-reports/${input.progressReportId}`,
    {
      method: "PATCH",
      body: formData,
    },
  )) as ProgressReportApiResponse<ApiProgressReport>;

  return { data: response.data, message: response.message };
}

export async function getProgressReports(): Promise<ProgressReportListResult> {
  const response = (await baseAPI(
    "/progress-reports?page=1&limit=100",
  )) as ProgressReportListResponse;

  return {
    rows: response.data.map(mapProgressReportToRow),
    calendarEvents: response.data.flatMap((report) =>
      mapProgressReportMeetingsToCalendarEvents(
        toMeetingList(report.meetings),
      ),
    ),
    meta: response.meta,
  };
}

// The report list only carries lightweight meetings, so the calendar popup
// fetches the full meeting when a card is opened.
export async function getProgressReportMeeting(
  meetingId: number,
): Promise<ApiProgressReportMeetingDetail> {
  const response = (await baseAPI(
    `/progress-report-meetings/${meetingId}`,
  )) as ProgressReportApiResponse<ApiProgressReportMeetingDetail>;

  return response.data;
}

export async function getProgressReport(
  progressReportId: number,
): Promise<ApiProgressReport> {
  const response = (await baseAPI(
    `/progress-reports/${progressReportId}`,
  )) as ProgressReportApiResponse<ApiProgressReport>;

  return response.data;
}

export async function getProgressReportMinistries(
  progressReportId: number,
): Promise<ApiProgressReportMinistryAssignment[]> {
  const response = (await baseAPI(
    `/progress-reports/${progressReportId}/ministries?page=1&limit=100&submittedToCdc=true`,
  )) as ProgressReportMinistryListResponse;

  return response.data;
}

export async function getProgressReportMinistryDetail(
  progressReportId: number,
  ministryId: number,
): Promise<ApiCdcProgressReportMinistryDetail> {
  const response = (await baseAPI(
    `/progress-reports/${progressReportId}/ministries/${ministryId}`,
  )) as ProgressReportApiResponse<ApiCdcProgressReportMinistryDetail>;

  return response.data;
}

export async function reviewProgressReportMinistry(input: {
  progressReportId: number;
  ministryId: number;
  status: "CDC_UNDER_REVIEW" | "COMPLETED";
}): Promise<ApiCdcProgressReportMinistryDetail> {
  const response = (await baseAPI(
    `/progress-reports/${input.progressReportId}/ministries/${input.ministryId}`,
    {
      method: "PATCH",
      body: JSON.stringify({ status: input.status }),
    },
  )) as ProgressReportApiResponse<ApiCdcProgressReportMinistryDetail>;

  return response.data;
}

export async function updateProgressReportMinistryIssueStatus(input: {
  progressReportId: number;
  ministryId: number;
  issueId: number;
  issueStatusId: number;
}): Promise<{
  issueId: number;
  issueStatus: {
    id: number;
    code: "IN_PROGRESS" | "NOT_ADDRESSED" | "SOLVED";
    name: string;
  };
}> {
  const response = (await baseAPI(
    `/progress-reports/${input.progressReportId}/ministries/${input.ministryId}/issues/${input.issueId}/status`,
    {
      method: "PATCH",
      body: JSON.stringify({ issueStatusId: input.issueStatusId }),
    },
  )) as ProgressReportApiResponse<{
    issueId: number;
    issueStatus: {
      id: number;
      code: "IN_PROGRESS" | "NOT_ADDRESSED" | "SOLVED";
      name: string;
    };
  }>;

  return response.data;
}

export async function getProgressReportCommentTypes(): Promise<ApiCommentType[]> {
  const response = (await baseAPI(
    "/comment-types",
  )) as ProgressReportApiResponse<ApiCommentType[]>;

  return response.data;
}

export async function createProgressReportIssueComment(input: {
  progressReportId: number;
  ministryId: number;
  issueId: number;
  commentTypeId: number;
  comment: string;
}): Promise<ApiProgressReportIssueComment> {
  const response = (await baseAPI(
    `/progress-reports/${input.progressReportId}/ministries/${input.ministryId}/issues/${input.issueId}/comments`,
    {
      method: "POST",
      body: JSON.stringify({
        commentTypeId: input.commentTypeId,
        comment: input.comment,
      }),
    },
  )) as ProgressReportApiResponse<ApiProgressReportIssueComment>;

  return response.data;
}

export async function updateProgressReportIssueComment(input: {
  progressReportId: number;
  ministryId: number;
  issueId: number;
  commentId: number;
  commentTypeId: number;
  comment: string;
}): Promise<ApiProgressReportIssueComment> {
  const response = (await baseAPI(
    `/progress-reports/${input.progressReportId}/ministries/${input.ministryId}/issues/${input.issueId}/comments/${input.commentId}`,
    {
      method: "PATCH",
      body: JSON.stringify({
        commentTypeId: input.commentTypeId,
        comment: input.comment,
      }),
    },
  )) as ProgressReportApiResponse<ApiProgressReportIssueComment>;

  return response.data;
}

export async function deleteProgressReportIssueComment(input: {
  progressReportId: number;
  ministryId: number;
  issueId: number;
  commentId: number;
}): Promise<void> {
  await baseAPI(
    `/progress-reports/${input.progressReportId}/ministries/${input.ministryId}/issues/${input.issueId}/comments/${input.commentId}`,
    { method: "DELETE" },
  );
}

export async function createProgressReportRgcDecisionComment(input: {
  progressReportId: number;
  ministryId: number;
  plenaryDecisionId: number;
  commentTypeId: number;
  comment: string;
}): Promise<ApiProgressReportIssueComment> {
  const response = (await baseAPI(
    `/progress-reports/${input.progressReportId}/ministries/${input.ministryId}/rgc-decisions/${input.plenaryDecisionId}/comments`,
    {
      method: "POST",
      body: JSON.stringify({
        commentTypeId: input.commentTypeId,
        comment: input.comment,
      }),
    },
  )) as ProgressReportApiResponse<ApiProgressReportIssueComment>;

  return response.data;
}

export async function updateProgressReportRgcDecisionComment(input: {
  progressReportId: number;
  ministryId: number;
  plenaryDecisionId: number;
  commentId: number;
  commentTypeId: number;
  comment: string;
}): Promise<ApiProgressReportIssueComment> {
  const response = (await baseAPI(
    `/progress-reports/${input.progressReportId}/ministries/${input.ministryId}/rgc-decisions/${input.plenaryDecisionId}/comments/${input.commentId}`,
    {
      method: "PATCH",
      body: JSON.stringify({
        commentTypeId: input.commentTypeId,
        comment: input.comment,
      }),
    },
  )) as ProgressReportApiResponse<ApiProgressReportIssueComment>;

  return response.data;
}

export async function deleteProgressReportRgcDecisionComment(input: {
  progressReportId: number;
  ministryId: number;
  plenaryDecisionId: number;
  commentId: number;
}): Promise<void> {
  await baseAPI(
    `/progress-reports/${input.progressReportId}/ministries/${input.ministryId}/rgc-decisions/${input.plenaryDecisionId}/comments/${input.commentId}`,
    { method: "DELETE" },
  );
}

export async function updateProgressReportMinistryRgcDecisionStatus(input: {
  progressReportId: number;
  ministryId: number;
  plenaryDecisionId: number;
  status: "NOT_ADDRESSED" | "IN_PROGRESS" | "SOLVED";
}): Promise<{ plenaryDecisionId: number; status: string }> {
  const response = (await baseAPI(
    `/progress-reports/${input.progressReportId}/ministries/${input.ministryId}/rgc-decisions/${input.plenaryDecisionId}/status`,
    {
      method: "PATCH",
      body: JSON.stringify({ status: input.status }),
    },
  )) as ProgressReportApiResponse<{
    plenaryDecisionId: number;
    status: string;
  }>;

  return response.data;
}

const cdcMinistryStatusLabels: Partial<
  Record<ProgressReportMinistryStatus, string>
> = {
  SUBMITTED: "Submitted",
  CDC_UNDER_REVIEW: "Under Review",
  COMPLETED: "Completed",
};

export function getCdcMinistryStatusLabel(
  status: ProgressReportMinistryStatus,
) {
  return cdcMinistryStatusLabels[status] ?? "-";
}
