import {
  IN_PROGRESS_COLOR,
  NOT_ADDRESSED_COLOR,
  SOLVED_COLOR,
  TOTAL_COLOR,
  type DonutChartItem,
  type StatusCountItem,
} from "@/features/ministry/dashboard/dashboard-data";
import type { AgencyStatusRow } from "@/features/ministry/dashboard/components/charts/agency-status-card";
import type { WorkingGroupStatusRow } from "@/features/ministry/dashboard/components/charts/dashboard-status-card";
import type { CategoryIssueRow } from "@/features/ministry/dashboard/components/charts/category-issues-card";

import type { CdcGpsfDashboardFiltersValue } from "./cdc-gpsf-dashboard-data";
import type {
  DashboardAgencyRow,
  DashboardCards,
  DashboardCategoryRow,
  DashboardStatusCount,
  DashboardSummaryParams,
  DashboardWorkingGroupRow,
} from "./service/cdc-gpsf-dashboard-service";

// These small functions reshape the API payload into the row shapes the
// chart cards already understand. Keeping them here (instead of inside the
// screen) makes each one easy to read and test on its own.

// Adds up the count for one status code inside a byStatus list.
function countByCode(byStatus: DashboardStatusCount[], code: string) {
  return byStatus
    .filter((slice) => slice.code === code)
    .reduce((sum, slice) => sum + slice.count, 0);
}

// Keep the row charts short: show at most this many rows. When the list is
// longer, everything past this point is merged into one "Other" row so a
// long list cannot stretch the page. (The API rows arrive sorted busiest
// first, so the merged rows are always the least busy ones.)
const MAX_CHART_ROWS = 10;

// Donut chart slices. Always the same 3 statuses in the original design
// colors (green/yellow/red) — matching the mock exactly — with a status
// left out entirely once none of its issues exist yet, instead of adding
// extra colors for Draft/Saved/New Submission.
export function mapCardsToDonut(cards: DashboardCards): DonutChartItem[] {
  return [
    { label: "Solved", value: cards.solved, color: SOLVED_COLOR },
    { label: "In Progress", value: cards.inProgress, color: IN_PROGRESS_COLOR },
    { label: "Not Addressed", value: cards.notAddressed, color: NOT_ADDRESSED_COLOR },
  ].filter((slice) => slice.value > 0);
}

// Legend rows next to the donut (Total / Solved / In Progress / Not Addressed).
export function mapCardsToStatusCounts(
  cards: DashboardCards,
): StatusCountItem[] {
  return [
    {
      label: "Total Issues",
      count: String(cards.totalIssues),
      color: TOTAL_COLOR,
    },
    { label: "Solved", count: String(cards.solved), color: SOLVED_COLOR },
    {
      label: "In Progress",
      count: String(cards.inProgress),
      color: IN_PROGRESS_COLOR,
    },
    {
      label: "Not Addressed",
      count: String(cards.notAddressed),
      color: NOT_ADDRESSED_COLOR,
    },
  ];
}

// Agency bar chart rows. The *Display fields are 0..1 fractions that drive
// the bar heights. Guard against dividing by zero: an agency whose issues
// are all in other statuses keeps its axis label but shows no bar.
export function mapAgenciesToAgencyRows(
  byPrimaryAgency: DashboardAgencyRow[],
): AgencyStatusRow[] {
  return byPrimaryAgency.map((agency) => {
    const solved = countByCode(agency.byStatus, "SOLVED");
    const inProgress = countByCode(agency.byStatus, "IN_PROGRESS");
    const notAddressed = countByCode(agency.byStatus, "NOT_ADDRESSED");
    const shownTotal = solved + inProgress + notAddressed;

    return {
      name: agency.agencyName,
      solved,
      inProgress,
      notAddressed,
      solvedDisplay: shownTotal === 0 ? 0 : solved / shownTotal,
      inProgressDisplay: shownTotal === 0 ? 0 : inProgress / shownTotal,
      notAddressedDisplay: shownTotal === 0 ? 0 : notAddressed / shownTotal,
    };
  });
}

// Working group stacked bars. Raw counts are fine: the row component
// divides each segment by the row total itself.
export function mapWorkingGroupsToRows(
  byWorkingGroup: DashboardWorkingGroupRow[],
): WorkingGroupStatusRow[] {
  const allRows = byWorkingGroup.map((group) => ({
    label: group.workingGroupName,
    solved: countByCode(group.byStatus, "SOLVED"),
    inProgress: countByCode(group.byStatus, "IN_PROGRESS"),
    notAddressed: countByCode(group.byStatus, "NOT_ADDRESSED"),
  }));

  if (allRows.length <= MAX_CHART_ROWS) return allRows;

  // Merge everything past the top rows into a single "Other" row.
  const topRows = allRows.slice(0, MAX_CHART_ROWS);
  const restRows = allRows.slice(MAX_CHART_ROWS);
  topRows.push({
    label: "Other",
    solved: restRows.reduce((sum, row) => sum + row.solved, 0),
    inProgress: restRows.reduce((sum, row) => sum + row.inProgress, 0),
    notAddressed: restRows.reduce((sum, row) => sum + row.notAddressed, 0),
  });
  return topRows;
}

// Category bar chart rows.
export function mapCategoriesToRows(
  byCategory: DashboardCategoryRow[],
): CategoryIssueRow[] {
  const allRows = byCategory.map((category) => ({
    label: category.categoryName,
    value: category.count,
  }));

  if (allRows.length <= MAX_CHART_ROWS) return allRows;

  // Merge everything past the top rows into a single "Other" row.
  const topRows = allRows.slice(0, MAX_CHART_ROWS);
  const restRows = allRows.slice(MAX_CHART_ROWS);
  topRows.push({
    label: "Other",
    value: restRows.reduce((sum, row) => sum + row.value, 0),
  });
  return topRows;
}

// Turns the dropdown state (strings) into API params (numbers). An empty
// string means "no filter", so the field is left out entirely — the backend
// returns 400 for unknown or badly-typed params.
// `report` is intentionally never sent: the backend has no column for it.
export function buildDashboardSummaryParams(
  filters: CdcGpsfDashboardFiltersValue,
): DashboardSummaryParams {
  const params: DashboardSummaryParams = {};

  if (filters.year) params.year = Number(filters.year);
  if (filters.status) params.issueStatusId = Number(filters.status);
  if (filters.agency) params.primaryAgencyId = Number(filters.agency);
  if (filters.category) params.categoryId = Number(filters.category);
  if (filters.workingGroup) params.workingGroupId = Number(filters.workingGroup);

  return params;
}
