import { MMCQ } from "@vibrant/quantizer-mmcq";
import type { CanvasState } from "@dada/shared-contracts";
import { P0A_COLOR_CARD_DEFINITIONS, type ColorCardDefinition } from "@dada/asset-renderer";

import type { CanvasElementIdentity } from "./editor-elements.js";

type CanvasElement = CanvasState["elements"][number];

export type { ColorCardDefinition } from "@dada/asset-renderer";

export interface PaletteColor {
  hex: string;
  population: number;
  rgbValue: number;
}

export const P0A_COLOR_CARDS: readonly ColorCardDefinition[] = P0A_COLOR_CARD_DEFINITIONS;

// Coordinates are translated from the archived 320x320 renderer around its 160px center.
export const COLOR_CARD_SOURCE_GEOMETRY = {
  style_01: {
    bounds: { bottom: 76, left: -26, right: 26, top: -74 },
    palette: { bottom: 37, left: -23, right: 23, top: -71 },
  },
  style_02: { bounds: { bottom: 69, left: -18, right: 18, top: -77 } },
  style_08: { bounds: { bottom: 10, left: -73, right: 73, top: -9 } },
  style_16: { bounds: { bottom: 9, left: -78, right: 78, top: -9 } },
} as const;

function normalizedHex(value: string) {
  return value.toUpperCase();
}

export function extractMmcqPalette(pixels: Uint8ClampedArray): PaletteColor[] {
  const swatches = MMCQ(pixels, { colorCount: 10 });
  if (swatches.length < 5) throw new Error("palette_five_colors_required");
  return swatches
    .map((swatch) => ({
      hex: normalizedHex(swatch.hex),
      population: swatch.population,
      rgbValue: (Math.round(swatch.r) << 16) + (Math.round(swatch.g) << 8) + Math.round(swatch.b),
    }))
    .sort((left, right) => right.population - left.population || left.rgbValue - right.rgbValue)
    .slice(0, 5);
}

export function extractPaletteFromImage(image: CanvasImageSource & { height: number; width: number }) {
  const longestEdge = Math.max(image.width, image.height);
  if (longestEdge <= 0) throw new Error("palette_image_invalid");
  const scale = Math.min(1, 256 / longestEdge);
  const width = Math.max(1, Math.round(image.width * scale));
  const height = Math.max(1, Math.round(image.height * scale));
  const surface = document.createElement("canvas");
  surface.width = width;
  surface.height = height;
  const context = surface.getContext("2d", { willReadFrequently: true });
  if (!context) throw new Error("palette_canvas_unavailable");
  context.drawImage(image, 0, 0, width, height);
  return extractMmcqPalette(context.getImageData(0, 0, width, height).data).map((entry) => entry.hex);
}

function validatePalette(palette: readonly string[]) {
  if (palette.length !== 5 || palette.some((color) => !/^#[0-9A-Fa-f]{6}$/.test(color))) throw new Error("canvas_palette_invalid");
  return palette.map(normalizedHex);
}

export function createColorCardElement(
  definition: ColorCardDefinition,
  palette: readonly string[],
  identity: CanvasElementIdentity,
  zIndex: number,
): CanvasElement {
  return {
    colors: validatePalette(palette),
    created_at: identity.createdAt,
    element_id: identity.elementId,
    opacity: 1,
    position: { x: 0.5, y: 0.5 },
    resource_version: "wp4-provider-v1",
    rotation: 0,
    scale: { x: 1, y: 1 },
    style_id: definition.styleId,
    style_parameters: {
      mapping_status: definition.mappingStatus,
      palette_algorithm_version: "mmcq-v1",
      renderer_name: definition.rendererName,
    },
    template_or_asset_id: definition.cardId,
    type: "color_card",
    z_index: zIndex,
  };
}

export function refreshColorCards(state: CanvasState, palette: readonly string[]): CanvasState {
  const colors = validatePalette(palette);
  const next = structuredClone(state);
  next.elements = next.elements.map((element) => element.type === "color_card" ? { ...element, colors: [...colors] } : element);
  return next;
}

export function drawColorCard(context: CanvasRenderingContext2D, element: CanvasElement) {
  const colors = element.colors ?? ["#111111", "#333333", "#555555", "#777777", "#999999"];
  if (element.style_id === "style_01") {
    context.fillStyle = "#ffffff";
    context.fillRect(-26, -74, 52, 145);
    colors.forEach((color, index) => {
      const top = -71 + index * 21.6;
      context.fillStyle = color;
      context.fillRect(-23, top, 46, index === colors.length - 1 ? 37 - top : 21.6);
    });
    context.fillStyle = "#222222";
    context.font = "700 5px Arial, sans-serif";
    context.textAlign = "left";
    context.textBaseline = "top";
    context.fillText("COLOR", -20, 44);
    context.fillStyle = "#979797";
    context.fillRect(-20, 54, 24, 1);
    context.fillStyle = "#ffffff";
    for (let index = 0; index < 8; index += 1) {
      const left = -26 + index * 6.5;
      context.beginPath();
      context.moveTo(left, 71);
      context.lineTo(left + 3.25, 76);
      context.lineTo(left + 6.5, 71);
      context.closePath();
      context.fill();
    }
    return;
  }
  if (element.style_id === "style_02") {
    context.font = "4px Arial, sans-serif";
    context.textAlign = "left";
    context.textBaseline = "top";
    context.fillStyle = "#222222";
    context.fillText("C O L O R", -17, -77);
    colors.forEach((color, index) => {
      context.fillStyle = color;
      context.fillRect(-18, -71 + index * 28, 36, 27);
    });
    return;
  }
  if (element.style_id === "style_08") {
    colors.forEach((color, index) => {
      const left = -73 + index * 29.2;
      const center = -59 + index * 29;
      context.fillStyle = color;
      context.fillRect(left, -2, index === colors.length - 1 ? 73 - left : 29.2, 4);
      context.beginPath();
      context.arc(center, -7, 2, 0, Math.PI * 2);
      context.fill();
      context.strokeStyle = "#ffffff";
      context.lineWidth = 1;
      context.stroke();
      context.fillStyle = "#222222";
      context.font = "4px Arial, sans-serif";
      context.textAlign = "center";
      context.textBaseline = "middle";
      context.fillText(`0${index + 1}`, center, 8);
    });
    return;
  }
  context.fillStyle = "#ffffff";
  context.beginPath();
  context.moveTo(-78, -9);
  for (let index = 0; index < 4; index += 1) {
    const y = -9 + index * 4.5;
    context.lineTo(-74, y + 2.25);
    context.lineTo(-78, y + 4.5);
  }
  context.lineTo(-70, 9);
  context.lineTo(-70, -9);
  context.closePath();
  context.fill();
  context.fillStyle = "#222222";
  context.fillRect(-72, -9, 17, 18);
  context.fillStyle = "#ffffff";
  context.font = "700 5px Arial, sans-serif";
  context.textAlign = "center";
  context.textBaseline = "middle";
  context.fillText("C", -63.5, 0);
  const stripColors = [...colors, "#f2f2f2"];
  stripColors.forEach((color, index) => {
    const left = -55 + index * (133 / stripColors.length);
    context.fillStyle = color;
    context.fillRect(left, -9, index === stripColors.length - 1 ? 78 - left : 133 / stripColors.length, 18);
  });
}
