import {
  ministryProgressReportDecisionRows,
  type MinistryProgressReportDecisionRow,
} from "./components/view-detail/progress-report-decisions-data";

import {
  type MinistryProgressReportIssueRow,
} from "./components/view-detail/progress-report-issues-data";
import type {
  ApiMinistryFlatOpenIssue,
  ApiMinistryOpenIssue,
  ApiMinistryProgressReportAssignment,
  ApiMinistryProgressReportDetail,
  ApiMinistryRgcDecision,
  ApiProgressReportRgcDecision,
} from "./service/progress-report-service";
import { formatCalendarTimeRange } from "@/components/ui/month-calendar";
import { formatDate, type DateLanguage } from "@/lib/date-utils";
import {
  getDocumentUrl,
  type UploadedFileMetadata,
} from "@/lib/document-file";
import { formatNumber } from "@/lib/number-utils";

export type MinistryProgressReportDetail = {
  id: number;
  title: string;
  year: string;
  ministryName: string;
  ministryShortName: string;
  ministryLogo: string | null;
  description: string;
  submittedDate: string;
  preparedBy: string;
  status: string;
  reviewedBy: string;
  latestUpdate: string;
  requestDocument: UploadedFileMetadata | null;
  approvalDocument: UploadedFileMetadata | null;
  meetingDateTime: string;
  location: string;
};

function decodeHtmlEntities(value: string) {
  return value
    .replaceAll("&nbsp;", " ")
    .replaceAll("&amp;", "&")
    .replaceAll("&lt;", "<")
    .replaceAll("&gt;", ">")
    .replaceAll("&quot;", '"')
    .replaceAll("&#39;", "'");
}

function formatDescription(value: unknown) {
  if (value === null || value === undefined) return "-";

  const text = typeof value === "string" ? value : JSON.stringify(value);
  const plainText = decodeHtmlEntities(text)
    .replace(/<[^>]*>/g, " ")
    .replace(/\s+/g, " ")
    .trim();

  return plainText || "-";
}

// Uploaded stakeholder/ministry logos are served by the backend. Static
// frontend images such as /images/logo/maff.png must stay on the frontend
// origin, so only /uploads/ paths get prefixed with the backend URL.
function resolveLogoUrl(value: unknown): string | null {
  if (typeof value !== "string") return null;

  const logoPath = value.trim();

  if (!logoPath) return null;

  return logoPath.startsWith("/uploads/")
    ? getDocumentUrl(logoPath)
    : logoPath;
}

export function mapApiProgressReportToDetail(
  assignment: ApiMinistryProgressReportDetail,
  language: DateLanguage = "en",
): MinistryProgressReportDetail {
  const report = assignment.progressReport;
  const latestMeeting = assignment.cdcInformation.meeting;
  const meetingDateTime = latestMeeting
    ? `${formatDate(latestMeeting.meetingDate, language)} | ${formatCalendarTimeRange(
        latestMeeting.startTime,
        latestMeeting.endTime,
        language,
      )}`
    : "-";

  return {
    id: report.id,
    title: report.title,
    year: formatNumber(report.year, language),
    ministryName:
      assignment.ministry.description?.trim() || assignment.ministry.name,
    ministryShortName: assignment.ministry.name,
    ministryLogo: resolveLogoUrl(assignment.ministry.logo),
    description: formatDescription(assignment.description),
    submittedDate: assignment.ministryInformation.submittedAt
      ? formatDate(assignment.ministryInformation.submittedAt, language)
      : "-",
    preparedBy:
      assignment.ministryInformation.preparedBy?.name?.trim() || "-",
    status: assignment.cdcInformation.status
      ? formatMinistryStatus(assignment.cdcInformation.status)
      : "-",
    reviewedBy: assignment.cdcInformation.reviewedBy?.name?.trim() || "-",
    latestUpdate: assignment.cdcInformation.latestUpdatedAt
      ? formatDate(assignment.cdcInformation.latestUpdatedAt, language)
      : "-",
    requestDocument: assignment.cdcInformation.requestDocument,
    approvalDocument:
      assignment.ministryInformation.approvalProgressReport,
    meetingDateTime,
    location: latestMeeting?.location.trim() || "-",
  };
}

// CDC and PSWG use the broader assignment response. Ministry uses the nested
// view model above, but both screens share the same presentation component.
export function mapApiAssignmentToDetail(
  assignment: ApiMinistryProgressReportAssignment,
  language: DateLanguage = "en",
): MinistryProgressReportDetail {
  const report = assignment.progressReport;
  const latestMeeting = report.latestMeeting;

  return {
    id: report.id,
    title: report.title,
    year: formatNumber(report.year, language),
    ministryName:
      assignment.ministry.description?.trim() || assignment.ministry.name,
    ministryShortName: assignment.ministry.name,
    ministryLogo: resolveLogoUrl(assignment.ministry.logo),
    description: formatDescription(report.description),
    submittedDate: assignment.submittedAt
      ? formatDate(assignment.submittedAt, language)
      : "-",
    preparedBy: assignment.preparedBy?.name?.trim() || "-",
    status: formatMinistryStatus(assignment.status),
    reviewedBy: assignment.reviewedBy?.name?.trim() || "-",
    latestUpdate: assignment.submittedAt
      ? formatDate(assignment.updatedAt, language)
      : "-",
    requestDocument: report.requestDocument,
    approvalDocument: assignment.attachment,
    meetingDateTime: latestMeeting
      ? `${formatDate(latestMeeting.meetingDate, language)} | ${formatCalendarTimeRange(
          latestMeeting.startTime,
          latestMeeting.endTime,
          language,
        )}`
      : "-",
    location: latestMeeting?.location.trim() || "-",
  };
}

function formatMinistryStatus(
  status: ApiMinistryProgressReportDetail["status"],
) {
  const labels: Record<ApiMinistryProgressReportDetail["status"], string> = {
    DRAFT: "Draft",
    SHARED_WITH_PSWG: "Shared with PSWG",
    PSWG_REVIEWED: "PSWG Reviewed",
    SUBMITTED: "Submitted",
    CDC_UNDER_REVIEW: "Under Review",
    COMPLETED: "Completed",
  };

  return labels[status];
}

function getIssueStatus(
  code:
    | ApiMinistryOpenIssue["status"]["code"]
    | NonNullable<ApiMinistryOpenIssue["progressUpdate"]>["issueStatus"]["code"],
): MinistryProgressReportIssueRow["status"] {
  if (code === "SOLVED") return "Solved";
  return code === "IN_PROGRESS" ? "In Progress" : "Not Addressed";
}

function getProgressUpdateText(value: unknown): string {
  if (typeof value === "string") {
    return value.trim() || "No data";
  }

  return value === null || value === undefined ? "No data" : String(value);
}

function getAgency(
  issue: ApiMinistryOpenIssue,
  agencyOrder: number,
) {
  return issue.governmentAgencies.find(
    (agency) => agency.agencyOrder === agencyOrder,
  )?.stakeholder;
}

export function mapApiOpenIssuesToRows(
  issues: ApiMinistryOpenIssue[],
): MinistryProgressReportIssueRow[] {
  return issues.map((issue) => {
    const progressUpdate = issue.progressUpdate;
    const primaryAgency = getAgency(issue, 1);
    const secondAgency = getAgency(issue, 2);
    const thirdAgency = getAgency(issue, 3);
    const fourthAgency = getAgency(issue, 4);
    const fifthAgency = getAgency(issue, 5);

    return {
      id: issue.id,
      issueStatusId: issue.status.id,
      hasProgressUpdate: Boolean(progressUpdate),
      cdcComment: issue.cdcComment ?? null,
      pswg: issue.workingGroup.name.trim() || "-",
      issue: issue.title,
      category: issue.category.name,
      issueDescription: issue.description,
      recommendation: issue.recommendation,
      rgcDecision: getProgressUpdateText(progressUpdate?.rgcDecision),
      status: getIssueStatus(
        issue.status.code,
      ),
      progressSolution: getProgressUpdateText(
        progressUpdate?.progressSolution,
      ),
      indicators: getProgressUpdateText(progressUpdate?.indicators),
      dateOfIssueSolution:
        progressUpdate?.dateOfIssueSolution || "No data",
      sourceOfVerification:
        progressUpdate?.sourceOfVerification?.trim() || "No data",
      linkToVerificationSource:
        progressUpdate?.linkToVerificationSource?.trim() || "No data",
      implementationChallenges: getProgressUpdateText(
        progressUpdate?.implementationChallenges,
      ),
      request: getProgressUpdateText(progressUpdate?.requests),
      nextStep: getProgressUpdateText(progressUpdate?.nextStep),
      progressUpdateAttachment: progressUpdate?.attachment ?? null,
      primaryAgency: primaryAgency?.name || "No data",
      primaryAgencyLogo: resolveLogoUrl(primaryAgency?.logo),
      secondAgency: secondAgency?.name || "No data",
      secondAgencyLogo: resolveLogoUrl(secondAgency?.logo),
      thirdAgency: thirdAgency?.name || "No data",
      thirdAgencyLogo: resolveLogoUrl(thirdAgency?.logo),
      fourthAgency: fourthAgency?.name || "No data",
      fourthAgencyLogo: resolveLogoUrl(fourthAgency?.logo),
      fifthAgency: fifthAgency?.name || "No data",
      fifthAgencyLogo: resolveLogoUrl(fifthAgency?.logo),
    };
  });
}

// The Ministry's own /me detail returns the flattened ApiMinistryFlatOpenIssue:
// progress fields sit on the issue, status/category/working group are id/name,
// and government agencies are a small { agencyOrder, name, logo } array.
export function mapMinistryOpenIssuesToRows(
  issues: ApiMinistryFlatOpenIssue[],
): MinistryProgressReportIssueRow[] {
  return issues.map((issue) => {
    const agency = (order: number) =>
      issue.governmentAgencies.find((item) => item.agencyOrder === order);
    const primaryAgency = agency(1);
    const secondAgency = agency(2);
    const thirdAgency = agency(3);
    const fourthAgency = agency(4);
    const fifthAgency = agency(5);

    return {
      id: issue.id,
      issueStatusId: issue.issueStatusId,
      hasProgressUpdate: issue.hasProgressUpdate,
      cdcComment: issue.cdcComment ?? null,
      pswg: issue.workingGroup.trim() || "-",
      issue: issue.title,
      category: issue.category,
      issueDescription: issue.description,
      recommendation: issue.recommendation,
      rgcDecision: getProgressUpdateText(issue.rgcDecision),
      status: getIssueStatus(issue.status),
      progressSolution: getProgressUpdateText(issue.progressSolution),
      indicators: getProgressUpdateText(issue.indicators),
      dateOfIssueSolution: issue.dateOfIssueSolution || "No data",
      sourceOfVerification: issue.sourceOfVerification?.trim() || "No data",
      linkToVerificationSource:
        issue.linkToVerificationSource?.trim() || "No data",
      implementationChallenges: getProgressUpdateText(
        issue.implementationChallenges,
      ),
      request: getProgressUpdateText(issue.requests),
      nextStep: getProgressUpdateText(issue.nextStep),
      progressUpdateAttachment: issue.attachment ?? null,
      primaryAgency: primaryAgency?.name || "No data",
      primaryAgencyLogo: resolveLogoUrl(primaryAgency?.logo),
      secondAgency: secondAgency?.name || "No data",
      secondAgencyLogo: resolveLogoUrl(secondAgency?.logo),
      thirdAgency: thirdAgency?.name || "No data",
      thirdAgencyLogo: resolveLogoUrl(thirdAgency?.logo),
      fourthAgency: fourthAgency?.name || "No data",
      fourthAgencyLogo: resolveLogoUrl(fourthAgency?.logo),
      fifthAgency: fifthAgency?.name || "No data",
      fifthAgencyLogo: resolveLogoUrl(fifthAgency?.logo),
    };
  });
}

export function getMinistryProgressReportDecisionRows(): MinistryProgressReportDecisionRow[] {
  return ministryProgressReportDecisionRows;
}

function mapDecisionStatus(
  status: ApiProgressReportRgcDecision["status"],
): MinistryProgressReportDecisionRow["status"] {
  if (status === "SOLVED") return "Solved";
  if (status === "IN_PROGRESS") return "In Progress";
  return "Not Addressed";
}

function joinUnique(values: string[]) {
  const uniqueValues = Array.from(
    new Set(values.map((value) => value.trim()).filter(Boolean)),
  );

  return uniqueValues.join(", ") || "No data";
}

// Shared by the CDC-review and PSWG views, whose responses keep the nested
// (category object + linked issues + progressUpdate) decision shape.
export function mapApiRgcDecisionsToRows(
  decisions: ApiProgressReportRgcDecision[],
  ministry: Pick<
    ApiMinistryProgressReportAssignment["ministry"],
    "name" | "logo"
  >,
): MinistryProgressReportDecisionRow[] {
  return decisions.map((decision) => {
    const progressUpdate = decision.progressUpdate;

    return {
      id: decision.id,
      hasProgressUpdate:
        decision.hasProgressUpdate ?? Boolean(decision.progressUpdate),
      cdcComment: decision.cdcComment ?? null,
      pswg: joinUnique(
        decision.issues?.map((issue) => issue.workingGroup.name) ?? [],
      ),
      issue: joinUnique(decision.issues?.map((issue) => issue.title) ?? []),
      category: decision.category.name || "No data",
      meetingDate: decision.meetingDate,
      rgcDecision: decision.decision || "No data",
      status: mapDecisionStatus(decision.status),
      focalPerson: decision.focalPerson || "No data",
      progressSolution: getProgressUpdateText(
        progressUpdate?.progressSolution,
      ),
      indicators: getProgressUpdateText(progressUpdate?.indicators),
      sourceOfVerification:
        decision.verificationSource?.trim() || "No data",
      linkToVerificationSource:
        decision.verificationLink?.trim() || "No data",
      implementationChallenges: getProgressUpdateText(
        progressUpdate?.implementationChallenges,
      ),
      request: getProgressUpdateText(progressUpdate?.requests),
      nextStep: getProgressUpdateText(progressUpdate?.nextStep),
      dateOfIssueSolution:
        progressUpdate?.dateOfIssueSolution || "No data",
      progressUpdateAttachment: progressUpdate?.attachment ?? null,
      primaryAgency: ministry.name || "No data",
      primaryAgencyLogo: resolveLogoUrl(ministry.logo),
      secondAgency: "No data",
      thirdAgency: "No data",
      fourthAgency: "No data",
      fifthAgency: "No data",
    };
  });
}

// The Ministry's own /me detail returns the flattened ApiMinistryRgcDecision:
// category is a plain name and the progress fields sit directly on the
// decision. The Ministry decision table has no PSWG/Issue column.
export function mapMinistryRgcDecisionsToRows(
  decisions: ApiMinistryRgcDecision[],
  ministry: Pick<
    ApiMinistryProgressReportAssignment["ministry"],
    "name" | "logo"
  >,
): MinistryProgressReportDecisionRow[] {
  return decisions.map((decision) => ({
    id: decision.id,
    hasProgressUpdate: decision.hasProgressUpdate ?? false,
    cdcComment: decision.cdcComment ?? null,
    pswg: "No data",
    issue: "No data",
    category: decision.category || "No data",
    meetingDate: decision.meetingDate,
    rgcDecision: decision.decision || "No data",
    status: mapDecisionStatus(decision.status),
    focalPerson: decision.focalPerson || "No data",
    progressSolution: getProgressUpdateText(decision.progressSolution),
    indicators: getProgressUpdateText(decision.indicators),
    sourceOfVerification: decision.verificationSource?.trim() || "No data",
    linkToVerificationSource:
      decision.verificationLink?.trim() || "No data",
    implementationChallenges: getProgressUpdateText(
      decision.implementationChallenges,
    ),
    request: getProgressUpdateText(decision.requests),
    nextStep: getProgressUpdateText(decision.nextStep),
    dateOfIssueSolution: decision.dateOfIssueSolution || "No data",
    progressUpdateAttachment: decision.attachment ?? null,
    primaryAgency: ministry.name || "No data",
    primaryAgencyLogo: resolveLogoUrl(ministry.logo),
    secondAgency: "No data",
    thirdAgency: "No data",
    fourthAgency: "No data",
    fifthAgency: "No data",
  }));
}
