const defaultArrayColors = [
  '#99CCFF',
  '#99FFCC',
  '#CC99FF',
  '#FFCC99',
  '#FFFF99',
  '#CCFF99',
  '#99FF66',
  '#66FFCC',
  '#66CCFF',
  '#9999FF',
];

const defaultObjectColors = {
  open: '#FF0000',
  done: '#33CC00',
};

const distinctColorThreshold = 50;

type ObjectLabels = {
  open: string;
  done: string;
};

type ArrayResponseItem = {
  name?: string;
  anzahl?: number | string;
  agb_dyncol?: string | null;
};

type ObjectResponseData = {
  open?: number | string | null;
  done?: number | string | null;
};

type NormalizedPieData = {
  labels: string[];
  values: number[];
  backgroundColors: string[];
};

type NormalizeResult =
  | { isNoData: true }
  | { isNoData: false; data: NormalizedPieData };

export function normalizeKuchenResponse(
  data: unknown,
  objectLabels: ObjectLabels
): NormalizeResult {
  if (Array.isArray(data)) {
    return normalizeArrayResponse(data);
  }

  if (data && typeof data === 'object') {
    return normalizeObjectResponse(data as ObjectResponseData, objectLabels);
  }

  return { isNoData: true };
}

export function buildPieChartData(data: NormalizedPieData) {
  return {
    labels: data.labels,
    datasets: [
      {
        data: data.values,
        backgroundColor: data.backgroundColors,
      },
    ],
  };
}

function normalizeArrayResponse(data: ArrayResponseItem[]): NormalizeResult {
  if (data.length === 0) {
    return { isNoData: true };
  }

  const safeItems = data.map((item) =>
    item && typeof item === 'object' ? item : ({} as ArrayResponseItem)
  );

  const labels = safeItems.map((item) => String(item.name ?? ''));
  const values = safeItems.map((item) => Number(item.anzahl) || 0);
  const backgroundColors = buildArrayBackgroundColors(safeItems, defaultArrayColors);

  return {
    isNoData: false,
    data: { labels, values, backgroundColors },
  };
}

function normalizeObjectResponse(
  data: ObjectResponseData,
  objectLabels: ObjectLabels
): NormalizeResult {
  const rawOpen = data.open;
  const rawDone = data.done;

  // Keep legacy behavior: only treat as "no data" when both values are literal 0.
  if (rawOpen === 0 && rawDone === 0) {
    return { isNoData: true };
  }

  const slices = [
    {
      key: 'open' as const,
      label: objectLabels.open,
      value: Number(rawOpen) || 0,
      color: defaultObjectColors.open,
    },
    {
      key: 'done' as const,
      label: objectLabels.done,
      value: Number(rawDone) || 0,
      color: defaultObjectColors.done,
    },
  ];

  return {
    isNoData: false,
    data: {
      labels: slices.map((slice) => slice.label),
      values: slices.map((slice) => slice.value),
      backgroundColors: slices.map((slice) => slice.color),
    },
  };
}

function buildArrayBackgroundColors(
  items: ArrayResponseItem[],
  palette: string[]
): string[] {
  const fallbackColors = generateHarmoniousPalette(items.length, palette);
  const usedColors: string[] = [];

  return items.map((item, idx) => {
    const dynamicColor =
      typeof item.agb_dyncol === 'string' && item.agb_dyncol.trim().length > 0
        ? item.agb_dyncol
        : null;
    const preferredColor = dynamicColor
      ? dynamicColor.toLowerCase()
      : fallbackColors[idx] || palette[0];
    const normalizedPreferred = preferredColor.toLowerCase();

    if (!usedColors.includes(normalizedPreferred)) {
      usedColors.push(normalizedPreferred);
      return preferredColor;
    }

    const distinct = findDistinctColor(usedColors, distinctColorThreshold, palette);
    usedColors.push(distinct.toLowerCase());
    return distinct;
  });
}

function generateHarmoniousPalette(count: number, palette: string[]): string[] {
  if (count <= palette.length) {
    return palette.slice(0, count);
  }

  const colors = [];
  for (let i = 0; i < count; i += 1) {
    colors.push(palette[i % palette.length]);
  }
  return colors;
}

function hexToRgb(hex: string): [number, number, number] {
  let normalizedHex = hex.replace('#', '');
  if (normalizedHex.length === 3) {
    normalizedHex = normalizedHex
      .split('')
      .map((x) => x + x)
      .join('');
  }

  const numericValue = parseInt(normalizedHex, 16);
  if (Number.isNaN(numericValue)) {
    return [200, 200, 200];
  }

  return [
    (numericValue >> 16) & 255,
    (numericValue >> 8) & 255,
    numericValue & 255,
  ];
}

function colorDistance(hex1: string, hex2: string): number {
  const [r1, g1, b1] = hexToRgb(hex1.toLowerCase());
  const [r2, g2, b2] = hexToRgb(hex2.toLowerCase());
  const dr = r1 - r2;
  const dg = g1 - g2;
  const db = b1 - b2;
  return Math.sqrt(dr * dr + dg * dg + db * db);
}

function findDistinctColor(
  alreadyUsed: string[],
  threshold: number,
  palette: string[]
): string {
  const candidates = palette.filter((color) => !alreadyUsed.includes(color.toLowerCase()));

  for (const candidate of candidates) {
    const isDistinct = alreadyUsed.every(
      (usedColor) => colorDistance(candidate, usedColor) >= threshold
    );
    if (isDistinct) {
      return candidate;
    }
  }

  if (candidates.length > 0) {
    return candidates[0];
  }

  return palette[0];
}
