import type { PlenaryAgencyStatusRow } from "@/features/cdc-gpsf/plenary/dashboard/components/charts/agency-status-card";
import type { PlenaryDonutSlice } from "@/features/cdc-gpsf/plenary/dashboard/components/charts/implementation-status-card";
import type { StatusStackedRow } from "@/features/cdc-gpsf/plenary/dashboard/components/charts/status-stacked-card";
import {
  EARLY_PROGRESS_COLOR,
  IN_PROGRESS_COLOR,
  MID_PROGRESS_COLOR,
} from "@/features/cdc-gpsf/plenary/dashboard/plenary-data";
import { formatNumber, type NumberLanguage } from "@/lib/number-utils";

import type { CdcGpsfDashboardFiltersValue } from "@/features/cdc-gpsf/dashboard/cdc-gpsf-dashboard-data";
import type { DashboardStatusCode } from "@/features/cdc-gpsf/dashboard/cdc-gpsf-dashboard-mappers";

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

// Keep the bar charts short: everything past this many rows is merged into one
// "Other" row, the same rule the working group dashboard follows.
const MAX_CHART_ROWS = 10;

// The five numbers in the cards row.
export function mapPlenaryCardsToSummary(
  cards: DraftDashboardCards,
): DraftDashboardSummaryValues {
  return {
    totalIssues: cards.totalIssues,
    solved: cards.solved,
    inProgress: cards.inProgress,
    notAddressed: cards.notAddressed,
    totalPrimaryAgencies: cards.totalPrimaryAgencies,
    totalMinistries: cards.totalMinistries,
  };
}

// One slice of a donut, with its share written in the reader's own digits.
function slice(
  label: string,
  value: number,
  color: string,
  total: number,
  language: NumberLanguage,
): PlenaryDonutSlice {
  const percent = total === 0 ? 0 : (value / total) * 100;

  return {
    label,
    value,
    color,
    labelText: `${formatNumber(Number(percent.toFixed(2)), language)}%`,
  };
}

// "Overall Implementation Status": solved against in progress.
export function mapPlenaryOverallDonut(
  cards: DraftDashboardCards,
  labels: { solved: string; inProgress: string },
  solvedColor: string,
  language: NumberLanguage,
): PlenaryDonutSlice[] {
  // Solved against in progress only: not-addressed items stay out of this
  // donut, they are read from the summary card.
  const total = cards.solved + cards.inProgress;

  return [
    slice(labels.solved, cards.solved, solvedColor, total, language),
    slice(
      labels.inProgress,
      cards.inProgress,
      IN_PROGRESS_COLOR,
      total,
      language,
    ),
  ].filter((item) => item.value > 0);
}

// "In Progress Status": how CDC-GPSF judged the in-progress items. Items it
// has not judged yet are in neither half, so the donut can be smaller than the
// In Progress card — that gap is real, not a rounding error.
export function mapPlenaryInProgressDonut(
  cards: DraftDashboardCards,
  labels: { midProgress: string; earlyProgress: string },
  language: NumberLanguage,
): PlenaryDonutSlice[] {
  const mid = cards.midProgress ?? 0;
  const early = cards.earlyProgress ?? 0;
  const total = mid + early;

  return [
    slice(labels.midProgress, mid, MID_PROGRESS_COLOR, total, language),
    slice(labels.earlyProgress, early, EARLY_PROGRESS_COLOR, total, language),
  ].filter((item) => item.value > 0);
}

// The "Government Primary Agencies" bars. The *Display fields are 0..1
// fractions that set the bar heights.
export function mapPlenaryAgencyRows(
  rows: DraftDashboard["byPrimaryAgency"],
): PlenaryAgencyStatusRow[] {
  return rows.map((row) => {
    const shown = row.solved + row.inProgress + row.notAddressed || 1;

    return {
      name: row.agencyName,
      solved: row.solved,
      inProgress: row.inProgress,
      notAddressed: row.notAddressed,
      solvedDisplay: row.solved / shown,
      inProgressDisplay: row.inProgress / shown,
      notAddressedDisplay: row.notAddressed / shown,
    };
  });
}

// Merges the least busy rows into one "Other" row when a list is long.
function withOtherRow(
  rows: StatusStackedRow[],
  otherLabel: string,
): StatusStackedRow[] {
  if (rows.length <= MAX_CHART_ROWS) return rows;

  const topRows = rows.slice(0, MAX_CHART_ROWS);
  const restRows = rows.slice(MAX_CHART_ROWS);

  return [
    ...topRows,
    {
      label: otherLabel,
      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),
    },
  ];
}

// "Measure Category" bars, split by status like the working group chart.
export function mapPlenaryCategoryRows(
  rows: DraftDashboard["byCategory"],
  otherLabel: string,
): StatusStackedRow[] {
  return withOtherRow(
    rows.map((row) => ({
      label: row.categoryName,
      solved: row.solved,
      inProgress: row.inProgress,
      notAddressed: row.notAddressed,
    })),
    otherLabel,
  );
}

// "Working group Name" bars, split by status.
export function mapPlenaryWorkingGroupRows(
  rows: DraftDashboard["byWorkingGroup"],
  otherLabel: string,
): StatusStackedRow[] {
  return withOtherRow(
    rows.map((row) => ({
      label: row.workingGroupName,
      solved: row.solved,
      inProgress: row.inProgress,
      notAddressed: row.notAddressed,
    })),
    otherLabel,
  );
}

// Narrows the published plenary numbers in the browser, the same way the
// working group dashboard narrows its own: each dropdown filters its own
// chart, and the status dropdown applies everywhere.
export function applyPlenaryFilters(
  dashboard: DraftDashboard,
  filters: CdcGpsfDashboardFiltersValue,
  statusCode: DashboardStatusCode | null,
): DraftDashboard {
  // A row keeps only the picked status, so a filtered chart shows one colour.
  const keepStatus = <T extends {
    total: number;
    solved: number;
    inProgress: number;
    notAddressed: number;
  }>(
    row: T,
  ): T => {
    if (!statusCode) return row;

    const solved = statusCode === "SOLVED" ? row.solved : 0;
    const inProgress = statusCode === "IN_PROGRESS" ? row.inProgress : 0;
    const notAddressed =
      statusCode === "NOT_ADDRESSED" ? row.notAddressed : 0;

    return {
      ...row,
      solved,
      inProgress,
      notAddressed,
      total: solved + inProgress + notAddressed,
    };
  };

  const cards = statusCode
    ? {
        ...dashboard.cards,
        totalIssues:
          statusCode === "SOLVED"
            ? dashboard.cards.solved
            : statusCode === "IN_PROGRESS"
              ? dashboard.cards.inProgress
              : dashboard.cards.notAddressed,
        solved: statusCode === "SOLVED" ? dashboard.cards.solved : 0,
        inProgress:
          statusCode === "IN_PROGRESS" ? dashboard.cards.inProgress : 0,
        notAddressed:
          statusCode === "NOT_ADDRESSED" ? dashboard.cards.notAddressed : 0,
      }
    : dashboard.cards;

  return {
    ...dashboard,
    cards,
    byPrimaryAgency: dashboard.byPrimaryAgency
      .filter(
        (row) => !filters.agency || String(row.agencyId) === filters.agency,
      )
      .map(keepStatus),
    byWorkingGroup: dashboard.byWorkingGroup
      .filter(
        (row) =>
          !filters.workingGroup ||
          String(row.workingGroupId) === filters.workingGroup,
      )
      .map(keepStatus),
    byCategory: dashboard.byCategory
      .filter(
        (row) => !filters.category || String(row.categoryId) === filters.category,
      )
      .map(keepStatus),
  };
}
