import { baseAPI } from "@/lib/api";
import type { UploadedFileMetadata } from "@/lib/document-file";

import type { ProgressReportRow } from "../progress-report-data";

// Ministries only read progress reports (they cannot create, edit, or send
// them), so this service contains only list and detail read operations.
export type ProgressReportSemester = "S1" | "S2";
export type ProgressReportStatus = "DRAFT" | "SENT";
export type ProgressReportMinistryStatus =
  | "DRAFT"
  | "SHARED_WITH_PSWG"
  | "PSWG_REVIEWED"
  | "SUBMITTED"
  | "CDC_UNDER_REVIEW"
  | "COMPLETED";

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

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

// 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 ApiProgressReportMeeting = {
  id: number;
  slot: ProgressReportMeetingSlot;
  title: string;
  meetingDate: string;
  startTime: string;
  endTime: string;
  status: ProgressReportStatus;
};

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

// The Ministry assignment endpoints are served by the ministry-progress-reports
// module, which still returns plain arrays with every meeting field.
export type ApiAssignmentProgressReportDeadline = {
  id: number;
  slot: ProgressReportDeadlineSlot;
  progressReportId: number;
  deadline: string;
};

export type ApiAssignmentProgressReportMeeting = {
  id: number;
  slot: ProgressReportMeetingSlot;
  progressReportId: number;
  title: string;
  description: unknown | null;
  meetingDate: string;
  startTime: string;
  endTime: string;
  location: string;
  documentReference: UploadedFileMetadata | null;
  status: ProgressReportStatus;
};

// The list endpoint attaches the caller ministry's own assignment to each
// report, so the ministry can see its personal workflow status per report.
export type ApiProgressReportMinistryAssignmentSummary = {
  id: number;
  ministryId: number;
  issues: number;
  attachment: UploadedFileMetadata | null;
  status: ProgressReportMinistryStatus;
  updatedAt: string;
};

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;
  ministryAssignment: ApiProgressReportMinistryAssignmentSummary | null;
};

export type ApiMinistryOpenIssue = {
  id: number;
  title: string;
  description: string;
  recommendation: string;
  status: {
    id: number;
    code: "IN_PROGRESS" | "NOT_ADDRESSED" | "SOLVED";
    name: string;
  };
  category: {
    id: number;
    name: string;
  };
  workingGroup: {
    id: number;
    name: string;
    logo: string | null;
  };
  governmentAgencies: Array<{
    agencyOrder: number;
    stakeholder: {
      id: number;
      name: string;
      description: string | null;
      logo: string | null;
    };
  }>;
  progressUpdate?: ApiProgressReportIssueUpdate | null;
  cdcComment?: ApiProgressReportIssueComment | null;
  createdAt: string;
  updatedAt: string;
};

export type ApiProgressReportIssueComment = {
  id: number;
  commentTypeId: number;
  commentType: {
    id: number;
    name: string;
  };
  comment: string;
  author: {
    id: number;
    name: string | null;
  };
  createdAt: string;
  updatedAt: string;
};

// The Ministry's own /me detail returns a flattened issue: status / category /
// working group collapse to id/name, government agencies keep only
// { agencyOrder, name, logo }, and the progress-update fields sit directly on the
// issue (null when no update yet). CDC and PSWG keep the nested
// ApiMinistryOpenIssue shape above.
export type ApiMinistryFlatOpenIssue = {
  id: number;
  title: string;
  description: string;
  recommendation: string;
  hasProgressUpdate?: boolean;
  issueStatusId: number;
  status: "IN_PROGRESS" | "NOT_ADDRESSED" | "SOLVED";
  category: string;
  workingGroup: string;
  governmentAgencies: Array<{
    agencyOrder: number;
    name: string;
    logo: string | null;
  }>;
  indicators: unknown | null;
  progressSolution: unknown | null;
  implementationChallenges: unknown | null;
  requests: unknown | null;
  sourceOfVerification: string | null;
  linkToVerificationSource: string | null;
  rgcDecision: unknown | null;
  nextStep: unknown | null;
  dateOfIssueSolution: string | null;
  attachment: UploadedFileMetadata | null;
  cdcComment?: ApiProgressReportIssueComment | null;
};

export type ApiProgressReportIssueUpdate = {
  id: number;
  progressReportId: number;
  ministryId: number;
  issueId: number | null;
  issueStatus: {
    id: number;
    code: "IN_PROGRESS" | "NOT_ADDRESSED" | "SOLVED";
    name: string;
  };
  indicators: unknown | null;
  progressSolution: unknown | null;
  implementationChallenges: unknown | null;
  requests: unknown | null;
  sourceOfVerification: string | null;
  linkToVerificationSource: string | null;
  rgcDecision: unknown | null;
  nextStep: unknown | null;
  dateOfIssueSolution: string | null;
  attachment: UploadedFileMetadata | null;
  updatedBy: {
    id: number;
    name: string | null;
  };
  createdAt: string;
  updatedAt: string;
};

export type ApiRgcDecisionStatus =
  | "NOT_ADDRESSED"
  | "IN_PROGRESS"
  | "SOLVED";

export type ApiProgressReportRgcDecisionUpdate = Omit<
  ApiProgressReportIssueUpdate,
  | "issueStatus"
  | "sourceOfVerification"
  | "linkToVerificationSource"
  | "rgcDecision"
>;

export type ApiProgressReportRgcDecision = {
  id: number;
  hasProgressUpdate?: boolean;
  cdcComment?: ApiProgressReportIssueComment | null;
  meetingDate: string;
  status: ApiRgcDecisionStatus;
  focalPerson: string;
  decision: string;
  category: { id: number; name: string };
  indicator?: {
    id: number;
    name: string;
    description: string | null;
  } | null;
  verificationSource?: string | null;
  verificationLink?: string | null;
  submittedToCdcAt?: string | null;
  // Present in the CDC-review and PSWG responses; omitted from the Ministry
  // /me response (which has no PSWG/Issue column).
  issues?: Array<{
    id: number;
    title: string;
    workingGroup: { id: number; name: string; logo: string | null };
  }>;
  progressUpdate?: ApiProgressReportRgcDecisionUpdate | null;
  createdAt: string;
  updatedAt: string;
};

// The Ministry's own /me detail returns a flattened RGC decision: category is a
// plain name and the progress-update fields sit directly on the decision (null
// when the Ministry has not saved progress yet). CDC and PSWG keep the richer
// nested ApiProgressReportRgcDecision shape above.
export type ApiMinistryRgcDecision = {
  id: number;
  hasProgressUpdate?: boolean;
  cdcComment?: ApiProgressReportIssueComment | null;
  meetingDate: string;
  status: ApiRgcDecisionStatus;
  focalPerson: string;
  decision: string;
  category: string;
  verificationSource: string | null;
  verificationLink: string | null;
  submittedToCdcAt: string | null;
  indicators: unknown | null;
  progressSolution: unknown | null;
  implementationChallenges: unknown | null;
  requests: unknown | null;
  nextStep: unknown | null;
  dateOfIssueSolution: string | null;
  attachment: UploadedFileMetadata | null;
};

export type ApiMinistryProgressReportAssignment = {
  id: number;
  progressReportId: number;
  ministryId: number;
  issues: number;
  openIssues: ApiMinistryOpenIssue[];
  attachment: UploadedFileMetadata | null;
  status: ProgressReportMinistryStatus;
  userId: number | null;
  submittedAt: string | null;
  ministry: {
    id: number;
    name: string;
    description: string | null;
    logo: string | null;
    relatedStakeholderId: number | null;
  };
  preparedBy: {
    id: number;
    name: string | null;
    email: string;
    position: string | null;
  } | null;
  reviewedBy: {
    id: number;
    name: string | null;
    position: string | null;
  } | null;
  progressReport: {
    id: number;
    title: string;
    description: unknown | null;
    year: number;
    semester: ProgressReportSemester;
    status: ProgressReportStatus;
    requestDocument: UploadedFileMetadata | null;
    draftSemesterReport: UploadedFileMetadata | null;
    finalSemesterReport: UploadedFileMetadata | null;
    deadlines: ApiAssignmentProgressReportDeadline[];
    meetings: ApiAssignmentProgressReportMeeting[];
    latestMeeting: ApiAssignmentProgressReportMeeting | null;
  };
  createdAt: string;
  updatedAt: string;
  deletedAt: string | null;
};

export type ApiMinistryProgressReportDetail = {
  id: number;
  progressReportId: number;
  status: ProgressReportMinistryStatus;
  progressReport: {
    id: number;
    title: string;
    year: number;
    semester: ProgressReportSemester;
  };
  ministry: {
    id: number;
    name: string;
    description: string | null;
    logo: string | null;
  };
  ministryInformation: {
    submittedAt: string | null;
    preparedBy: {
      id: number;
      name: string | null;
      position: string | null;
    } | null;
    approvalProgressReport: UploadedFileMetadata | null;
  };
  cdcInformation: {
    status: ProgressReportMinistryStatus | null;
    reviewedBy: {
      id: number;
      name: string | null;
      position: string | null;
    } | null;
    latestUpdatedAt: string | null;
    requestDocument: UploadedFileMetadata | null;
    meeting: {
      meetingDate: string;
      startTime: string;
      endTime: string;
      location: string;
    } | null;
  };
  description: unknown | null;
  issues: number;
  openIssues: ApiMinistryFlatOpenIssue[];
  rgcDecisions: ApiMinistryRgcDecision[];
};

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

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

type ProgressReportDetailResponse = {
  success: boolean;
  statusCode: number;
  message: string;
  data: ApiMinistryProgressReportDetail;
};

type ProgressReportAssignmentResponse = {
  success: boolean;
  statusCode: number;
  message: string;
  data: ApiMinistryProgressReportAssignment;
};

type ProgressReportIssueUpdateResponse = {
  success: boolean;
  statusCode: number;
  message: string;
  data: ApiProgressReportIssueUpdate;
};

type ProgressReportRgcDecisionUpdateResponse = {
  success: boolean;
  statusCode: number;
  message: string;
  data: ApiProgressReportRgcDecisionUpdate & {
    plenaryDecisionId: number;
    status: ApiRgcDecisionStatus;
    category: { id: number; name: string };
    indicator: {
      id: number;
      name: string;
      description: string | null;
    } | null;
    meetingDate: string;
    focalPerson: string;
    decision: string;
    verificationSource: string | null;
    verificationLink: string | null;
    submittedToCdcAt: string | null;
  };
};

// Display labels for the ministry's per-report workflow status. Mirrors the
// PSWG list's assignmentStatusLabels so both tables read consistently.
const ministryAssignmentStatusLabels: Record<
  ProgressReportMinistryStatus,
  string
> = {
  DRAFT: "Draft",
  SHARED_WITH_PSWG: "Shared",
  PSWG_REVIEWED: "PSWG Reviewed",
  SUBMITTED: "Submitted",
  CDC_UNDER_REVIEW: "Under Review",
  COMPLETED: "Completed",
};

export function mapProgressReportToRow(
  report: ApiProgressReport,
): ProgressReportRow {
  return {
    id: report.id,
    title: report.title,
    year: String(report.year),
    deadline: report.deadlines.firstDeadline?.date || "-",
    firstMeeting: report.meetings.firstMeeting?.meetingDate || "-",
    secondDeadline: report.deadlines.secondDeadline?.date || "-",
    secondMeeting: report.meetings.secondMeeting?.meetingDate || "-",
    thirdDeadline: report.deadlines.finalDeadline?.date || "-",
    draftSemester: report.draftSemesterReport,
    reportDoc: report.attachment,
    // Show the ministry's own workflow status for this report, not the
    // CDC-level "SENT/DRAFT" (which is always SENT in this list).
    status: report.ministryAssignment
      ? ministryAssignmentStatusLabels[report.ministryAssignment.status]
      : "Draft",
  };
}

// Ministries should only ever see reports CDC-GPSF has actually sent, so the
// status filter is hardcoded here rather than left to the caller.
export async function getSentProgressReports(): Promise<ProgressReportRow[]> {
  const response = (await baseAPI(
    "/progress-reports?status=SENT&page=1&limit=100",
  )) as ProgressReportListResponse;

  return response.data.map(mapProgressReportToRow);
}

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

  return response.data;
}

export type MinistryProgressReportPatchStatus = "DRAFT" | "SHARED_WITH_PSWG";

// A Ministry may upload or replace its report PDF while it is still a draft or
// after PSWG has reviewed it (before submitting to CDC).
export function canMinistryUploadDocument(
  status: ProgressReportMinistryStatus,
): boolean {
  return status === "DRAFT" || status === "PSWG_REVIEWED";
}

export async function updateMyProgressReport(input: {
  progressReportId: number;
  status?: MinistryProgressReportPatchStatus;
  file?: File;
}): Promise<ApiMinistryProgressReportAssignment> {
  const formData = new FormData();
  if (input.status) formData.append("status", input.status);

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

  const response = (await baseAPI(
    `/progress-reports/${input.progressReportId}/ministries/me`, {
      method: "PATCH",
      body: formData,
    },
  )) as ProgressReportAssignmentResponse;

  return response.data;
}

export async function submitMyProgressReport(
  progressReportId: number,
): Promise<ApiMinistryProgressReportDetail> {
  const response = (await baseAPI(
    `/progress-reports/${progressReportId}/ministries/me/submit`,
    { method: "POST" },
  )) as ProgressReportDetailResponse;

  return response.data;
}

export type UpsertMyProgressReportIssueInput = {
  progressReportId: number;
  issueId: number;
  issueStatusId: number;
  indicators?: string;
  progressSolution?: string;
  implementationChallenges?: string;
  requests?: string;
  sourceOfVerification?: string;
  linkToVerificationSource?: string;
  nextStep?: string;
  dateOfIssueSolution?: string;
  attachment?: File | null;
};

export async function upsertMyProgressReportIssue(
  input: UpsertMyProgressReportIssueInput,
): Promise<ApiProgressReportIssueUpdate> {
  const formData = new FormData();

  formData.append("issueStatusId", String(input.issueStatusId));

  const textFields = {
    indicators: input.indicators,
    progressSolution: input.progressSolution,
    implementationChallenges: input.implementationChallenges,
    requests: input.requests,
    sourceOfVerification: input.sourceOfVerification,
    linkToVerificationSource: input.linkToVerificationSource,
    nextStep: input.nextStep,
    dateOfIssueSolution: input.dateOfIssueSolution,
  };

  Object.entries(textFields).forEach(([key, value]) => {
    formData.append(key, value?.trim() ?? "");
  });

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

  const response = (await baseAPI(
    `/progress-reports/${input.progressReportId}/ministries/me/issues/${input.issueId}`,
    {
      method: "PUT",
      body: formData,
    },
  )) as ProgressReportIssueUpdateResponse;

  return response.data;
}

export type UpsertMyProgressReportRgcDecisionInput = {
  progressReportId: number;
  plenaryDecisionId: number;
  status: ApiRgcDecisionStatus;
  indicatorId?: number;
  category?: string;
  meetingDate?: string;
  focalPerson: string;
  decision: string;
  indicators?: string;
  progressSolution?: string;
  implementationChallenges?: string;
  requests?: string;
  sourceOfVerification?: string;
  linkToVerificationSource?: string;
  nextStep?: string;
  dateOfIssueSolution?: string;
  attachment?: File | null;
};

export async function upsertMyProgressReportRgcDecision(
  input: UpsertMyProgressReportRgcDecisionInput,
): Promise<ProgressReportRgcDecisionUpdateResponse["data"]> {
  const formData = new FormData();

  formData.append("status", input.status);
  formData.append("focalPerson", input.focalPerson.trim());
  formData.append("decision", input.decision.trim());

  const textFields = {
    indicatorId:
      input.indicatorId !== undefined ? String(input.indicatorId) : undefined,
    category: input.category,
    meetingDate: input.meetingDate,
    indicators: input.indicators,
    progressSolution: input.progressSolution,
    implementationChallenges: input.implementationChallenges,
    requests: input.requests,
    // Source of Verification / Link to Verification Source belong to the
    // master PlenaryRgcDecision (verificationSource / verificationLink), not
    // the per-report progress update.
    verificationSource: input.sourceOfVerification,
    verificationLink: input.linkToVerificationSource,
    nextStep: input.nextStep,
    dateOfIssueSolution: input.dateOfIssueSolution,
  };

  // Only send optional fields that actually have a value. Appending an empty
  // string makes the backend reject constrained fields (e.g. indicatorId's
  // @Min(1), category's @IsNotEmpty, meetingDate's @IsDateString).
  Object.entries(textFields).forEach(([key, value]) => {
    const trimmed = value?.trim();
    if (trimmed) formData.append(key, trimmed);
  });

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

  const response = (await baseAPI(
    `/progress-reports/${input.progressReportId}/ministries/me/rgc-decisions/${input.plenaryDecisionId}`,
    {
      method: "PATCH",
      body: formData,
    },
  )) as ProgressReportRgcDecisionUpdateResponse;

  return response.data;
}
