/*
 * Os elementos em SVG com cor trocável (como os do Canva): a peça é uma
 * imagem com `vetor` = as cores originais do desenho e as trocas que a pessoa
 * fez. Sem troca, entra o arquivo como está; com troca, o SVG (guardado aqui
 * depois de baixado uma vez) sai com cada cor trocada, num data: URL. O
 * editor, a página pública e o servidor pedem o texto antes de desenhar
 * (garantirVetores); quem ainda não tem o texto mostra o arquivo original.
 * Roda sem DOM.
 */

export type VetorDaPeca = { cores: string[]; trocas?: Record<string, string> | null };

/* As cores só nos lugares de cor (fill, stroke, stop-color...), nunca num "#abc123" de link interno. */
const COR = /(fill|stroke|stop-color|flood-color|lighting-color|color)(\s*[:=]\s*["']?)(#[0-9a-fA-F]{6})\b/g;
const HEX = /^#[0-9A-F]{6}$/;

const textos = new Map<string, string>();
const pedidos = new Map<string, Promise<void>>();
let versao = 0;
const ouvintes = new Set<() => void>();

/** Muda a cada texto novo guardado (o desenho guardado de cada peça confere). */
export const versaoDosVetores = () => versao;
/** Avisa quando chega texto novo (o editor redesenha). */
export function aoChegarVetor(f: () => void) {
  ouvintes.add(f);
  return () => {
    ouvintes.delete(f);
  };
}

/** As trocas que mudam alguma coisa (cor de verdade e diferente da original). */
export function trocasDoVetor(v: VetorDaPeca | null | undefined): Record<string, string> {
  const saida: Record<string, string> = {};
  for (const [de, para] of Object.entries(v?.trocas || {})) {
    const a = de.toUpperCase();
    const b = String(para).toUpperCase();
    if (HEX.test(a) && HEX.test(b) && a !== b) saida[a] = b;
  }
  return saida;
}

/** O vetor que vem do banco (cores em #RRGGBB, até 12). */
export function vetorLimpo(bruto: unknown): VetorDaPeca | null {
  if (!bruto || typeof bruto !== "object") return null;
  const v = bruto as { cores?: unknown; trocas?: unknown };
  const cores = (Array.isArray(v.cores) ? v.cores : []).map((c) => String(c).toUpperCase()).filter((c) => HEX.test(c)).slice(0, 12);
  if (!cores.length) return null;
  const trocas = trocasDoVetor({ cores, trocas: v.trocas && typeof v.trocas === "object" ? (v.trocas as Record<string, string>) : null });
  return Object.keys(trocas).length ? { cores, trocas } : { cores };
}

/** O SVG com as cores trocadas (todas de uma vez: trocar A→B e B→C não vira A→C). */
export function svgComCores(svg: string, trocas: Record<string, string>) {
  if (!Object.keys(trocas).length) return svg;
  return svg.replace(COR, (tudo, nome: string, meio: string, cor: string) => {
    const nova = trocas[cor.toUpperCase()];
    return nova ? `${nome}${meio}${nova}` : tudo;
  });
}

/** Guarda o texto de um SVG já baixado (o servidor usa ao embutir). */
export function guardarVetor(url: string, svg: string) {
  if (textos.get(url) === svg) return;
  textos.set(url, svg);
  versao += 1;
  ouvintes.forEach((f) => f());
}
export const textoDoVetor = (url: string) => textos.get(url);

/** Baixa (uma vez) o texto dos SVGs que têm cor trocada. */
export async function garantirVetores(urls: Iterable<string>): Promise<void> {
  const faltam = [...new Set(urls)].filter((u) => /^https:\/\//.test(u) && /\.svg(\?|$)/i.test(u) && !textos.has(u));
  await Promise.all(
    faltam.map((u) => {
      let p = pedidos.get(u);
      if (!p) {
        p = fetch(u)
          .then((r) => (r.ok ? r.text() : Promise.reject(new Error(String(r.status)))))
          .then((svg) => {
            if (/<svg[\s>]/i.test(svg)) guardarVetor(u, svg);
          })
          .catch(() => {
            pedidos.delete(u);
          });
        pedidos.set(u, p);
      }
      return p;
    }),
  );
}

/** Os SVGs com cor trocada destas peças (para pedir antes de desenhar). */
export function vetoresATrazer(pecas: Array<{ tipo: string; src?: string; vetor?: VetorDaPeca | null }>) {
  return pecas.filter((p) => p.tipo === "imagem" && p.src && Object.keys(trocasDoVetor(p.vetor)).length).map((p) => p.src as string);
}

/** O endereço que a peça desenha: o SVG recolorido (se o texto já está aqui) ou o de sempre. */
export function enderecoDoVetor(p: { src: string; vetor?: VetorDaPeca | null }, padrao: string) {
  const trocas = trocasDoVetor(p.vetor);
  if (!Object.keys(trocas).length) return padrao;
  const texto = textos.get(p.src);
  return texto ? `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgComCores(texto, trocas))}` : padrao;
}
