// Turns the draft-dashboard API payload into the props the existing chart
// cards already understand. Keeping the reshaping here (instead of inside the
// screen) makes each step easy to read on its own.

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

import { formatNumber, type NumberLanguage } from "@/lib/number-utils";

import type { DraftDashboardSummaryValues } from "./draft-dashboard-summary-cards";
import type {
  DraftDashboardAgencyRow,
  DraftDashboardCards,
  DraftDashboardCategoryRow,
  DraftDashboardWorkingGroupRow,
} from "./draft-dashboard-service";

// Keep the row charts short: show at most this many rows and merge the rest
// into one "Other" row. The API rows arrive busiest first, so the merged rows
// are always the least busy ones.
const MAX_CHART_ROWS = 10;

// The five summary cards at the top of the page.
export function mapCardsToSummary(
  cards: DraftDashboardCards,
): DraftDashboardSummaryValues {
  return {
    totalIssues: cards.totalIssues,
    solved: cards.solved,
    inProgress: cards.inProgress,
    notAddressed: cards.notAddressed,
    totalPrimaryAgencies: cards.totalPrimaryAgencies,
    totalMinistries: cards.totalMinistries,
  };
}

// The four words the donut and its legend are built from. The page passes
// them in its own language.
export type DraftDashboardStatusLabels = {
  totalIssues: string;
  solved: string;
  inProgress: string;
  notAddressed: string;
};

const defaultStatusLabels: DraftDashboardStatusLabels = {
  totalIssues: "Total Issues",
  solved: "Solved",
  inProgress: "In Progress",
  notAddressed: "Not Addressed",
};

// Donut slices. A status with no issues is left out so the chart keeps the
// three design colors instead of drawing an empty slice.
export function mapCardsToDonut(
  cards: DraftDashboardCards,
  labels: DraftDashboardStatusLabels = defaultStatusLabels,
  language: NumberLanguage = "en",
): DonutChartItem[] {
  const total = cards.solved + cards.inProgress + cards.notAddressed;

  // The slice text is a percentage, written in the page's own digits.
  const percentText = (value: number) => {
    const percent = total === 0 ? 0 : Math.round((value / total) * 100);

    return `${formatNumber(percent, language)}%`;
  };

  return [
    {
      label: labels.solved,
      value: cards.solved,
      color: SOLVED_COLOR,
      labelText: percentText(cards.solved),
    },
    {
      label: labels.inProgress,
      value: cards.inProgress,
      color: IN_PROGRESS_COLOR,
      labelText: percentText(cards.inProgress),
    },
    {
      label: labels.notAddressed,
      value: cards.notAddressed,
      color: NOT_ADDRESSED_COLOR,
      labelText: percentText(cards.notAddressed),
    },
  ].filter((slice) => slice.value > 0);
}

// Legend rows next to the donut (Total / Solved / In Progress / Not Addressed).
export function mapCardsToStatusCounts(
  cards: DraftDashboardCards,
  labels: DraftDashboardStatusLabels = defaultStatusLabels,
  language: NumberLanguage = "en",
): StatusCountItem[] {
  return [
    {
      label: labels.totalIssues,
      count: formatNumber(cards.totalIssues, language),
      color: TOTAL_COLOR,
    },
    {
      label: labels.solved,
      count: formatNumber(cards.solved, language),
      color: SOLVED_COLOR,
    },
    {
      label: labels.inProgress,
      count: formatNumber(cards.inProgress, language),
      color: IN_PROGRESS_COLOR,
    },
    {
      label: labels.notAddressed,
      count: formatNumber(cards.notAddressed, language),
      color: NOT_ADDRESSED_COLOR,
    },
  ];
}

// Agency bars. The *Display fields are 0..1 fractions that drive the bar
// heights, so an agency with no issues keeps its label but shows no bar.
export function mapAgenciesToRows(
  byPrimaryAgency: DraftDashboardAgencyRow[],
): AgencyStatusRow[] {
  return byPrimaryAgency.map((agency) => {
    const shownTotal = agency.solved + agency.inProgress + agency.notAddressed;

    return {
      name: agency.agencyName,
      solved: agency.solved,
      inProgress: agency.inProgress,
      notAddressed: agency.notAddressed,
      solvedDisplay: shownTotal === 0 ? 0 : agency.solved / shownTotal,
      inProgressDisplay: shownTotal === 0 ? 0 : agency.inProgress / shownTotal,
      notAddressedDisplay:
        shownTotal === 0 ? 0 : agency.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: DraftDashboardWorkingGroupRow[],
): WorkingGroupStatusRow[] {
  const allRows = byWorkingGroup.map((group) => ({
    label: group.workingGroupName,
    solved: group.solved,
    inProgress: group.inProgress,
    notAddressed: group.notAddressed,
  }));

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

  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 bars.
export function mapCategoriesToRows(
  byCategory: DraftDashboardCategoryRow[],
): CategoryIssueRow[] {
  const allRows = byCategory.map((category) => ({
    label: category.categoryName,
    value: category.total,
  }));

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

  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;
}
