export type ParsedColor = {
  r: number;
  g: number;
  b: number;
  /** 0–1 */
  alpha: number;
  isTransparent: boolean;
};

const DEFAULT_FALLBACK: ParsedColor = { r: 255, g: 255, b: 255, alpha: 1, isTransparent: false };

function clampByte(n: number): number {
  return Math.max(0, Math.min(255, Math.round(n)));
}

function clampAlpha(n: number): number {
  return Math.max(0, Math.min(1, n));
}

function expandHex(hex: string): string | null {
  const h = hex.replace(/^#/, "").trim();
  if (/^[0-9a-f]{3}$/i.test(h)) {
    return h
      .split("")
      .map((c) => c + c)
      .join("");
  }
  if (/^[0-9a-f]{6}$/i.test(h)) return h;
  if (/^[0-9a-f]{8}$/i.test(h)) return h;
  return null;
}

function parseHexColor(input: string): ParsedColor | null {
  const expanded = expandHex(input);
  if (!expanded) return null;
  const r = parseInt(expanded.slice(0, 2), 16);
  const g = parseInt(expanded.slice(2, 4), 16);
  const b = parseInt(expanded.slice(4, 6), 16);
  const alpha = expanded.length === 8 ? clampAlpha(parseInt(expanded.slice(6, 8), 16) / 255) : 1;
  return {
    r,
    g,
    b,
    alpha,
    isTransparent: alpha === 0,
  };
}

function parseRgbColor(input: string): ParsedColor | null {
  const match = input
    .trim()
    .match(/^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*(?:,\s*([\d.]+%?)\s*)?\)$/i);
  if (!match) return null;
  const r = clampByte(Number(match[1]));
  const g = clampByte(Number(match[2]));
  const b = clampByte(Number(match[3]));
  let alpha = 1;
  if (match[4] !== undefined) {
    const raw = match[4].trim();
    alpha = raw.endsWith("%") ? clampAlpha(Number(raw.slice(0, -1)) / 100) : clampAlpha(Number(raw));
  }
  return { r, g, b, alpha, isTransparent: alpha === 0 };
}

/** Parse CSS color strings used in PCO styles (#hex, #hex8, rgb/rgba, transparent). */
export function parseColor(input: string | undefined, fallback?: string): ParsedColor {
  const raw = (input ?? "").trim();
  if (!raw) {
    const fb = fallback?.trim();
    if (fb) {
      const parsed = parseColor(fb);
      if (!parsed.isTransparent || fb.toLowerCase() === "transparent") return parsed;
    }
    return { ...DEFAULT_FALLBACK };
  }
  if (raw.toLowerCase() === "transparent") {
    return { r: 0, g: 0, b: 0, alpha: 0, isTransparent: true };
  }
  if (raw.startsWith("#")) {
    return parseHexColor(raw) ?? { ...DEFAULT_FALLBACK };
  }
  if (raw.toLowerCase().startsWith("rgb")) {
    return parseRgbColor(raw) ?? { ...DEFAULT_FALLBACK };
  }
  const hex = parseHexColor(raw);
  if (hex) return hex;
  return { ...DEFAULT_FALLBACK };
}

function roundAlpha(alpha: number): string {
  const rounded = Math.round(alpha * 1000) / 1000;
  return String(rounded).replace(/\.?0+$/, "") || "0";
}

/** Format for storage — transparent, hex when opaque, rgba when partially transparent. */
export function formatColor(color: ParsedColor): string {
  if (color.isTransparent || color.alpha === 0) return "transparent";
  if (color.alpha >= 1) {
    const toHex = (n: number) => clampByte(n).toString(16).padStart(2, "0");
    return `#${toHex(color.r)}${toHex(color.g)}${toHex(color.b)}`;
  }
  return `rgba(${clampByte(color.r)}, ${clampByte(color.g)}, ${clampByte(color.b)}, ${roundAlpha(color.alpha)})`;
}

export function toColorInputHex(color: ParsedColor): string {
  const toHex = (n: number) => clampByte(n).toString(16).padStart(2, "0");
  return `#${toHex(color.r)}${toHex(color.g)}${toHex(color.b)}`;
}

export function colorToCssBackground(color: ParsedColor): string {
  if (color.isTransparent || color.alpha === 0) return "transparent";
  return formatColor(color);
}

export function opacityPercent(color: ParsedColor): number {
  return Math.round(color.alpha * 100);
}

export function withOpacity(color: ParsedColor, percent: number): ParsedColor {
  const alpha = clampAlpha(percent / 100);
  return {
    ...color,
    alpha,
    isTransparent: alpha === 0,
  };
}

export function withRgb(color: ParsedColor, hex: string): ParsedColor {
  const parsed = parseHexColor(hex);
  if (!parsed) return color;
  return {
    r: parsed.r,
    g: parsed.g,
    b: parsed.b,
    alpha: color.alpha,
    isTransparent: color.alpha === 0,
  };
}
