import sharp from "sharp";

import type { ImagemDoBanner, LogoDoBanner, TipoDaImagem } from "./gerador-de-banner-modelos";

/*
 * As imagens que entram no banner (foto do produto, imagem enviada, logo) e a
 * imagem que sai dele. Sem banco e sem "@/": o roteiro de amostras usa as
 * mesmas funções que a ferramenta.
 */

const LIMITE_DE_PIXELS = 60_000_000;

/* Produto em fundo branco, PNG sem fundo ou foto de cena: muda o desenho
   (cartão branco, solto com sombra, ou recortado na forma). Decide pela borda. */
export async function tipoDaImagem(conteudo: Buffer): Promise<TipoDaImagem> {
  return (await analisarImagem(conteudo)).tipo;
}

/** O tipo da imagem e a cor média da borda (o "fundo" da foto), como #RRGGBB. */
export async function analisarImagem(conteudo: Buffer): Promise<{ tipo: TipoDaImagem; fundo: string }> {
  const LADO = 48;
  const { data } = await sharp(conteudo, { limitInputPixels: LIMITE_DE_PIXELS })
    .resize(LADO, LADO, { fit: "fill" })
    .ensureAlpha()
    .raw()
    .toBuffer({ resolveWithObject: true });
  let borda = 0;
  let transparentes = 0;
  let brancos = 0;
  const soma = [0, 0, 0];
  const somaDosClaros = [0, 0, 0];
  let opacos = 0;
  for (let y = 0; y < LADO; y += 1) {
    for (let x = 0; x < LADO; x += 1) {
      if (x > 1 && x < LADO - 2 && y > 1 && y < LADO - 2) continue;
      const i = (y * LADO + x) * 4;
      borda += 1;
      if (data[i + 3] < 40) {
        transparentes += 1;
        continue;
      }
      opacos += 1;
      soma[0] += data[i];
      soma[1] += data[i + 1];
      soma[2] += data[i + 2];
      if (Math.min(data[i], data[i + 1], data[i + 2]) >= 228) {
        brancos += 1;
        somaDosClaros[0] += data[i];
        somaDosClaros[1] += data[i + 1];
        somaDosClaros[2] += data[i + 2];
      }
    }
  }
  const media = (canais: number[], quantos: number) => `#${canais.map((canal) => Math.round(canal / quantos).toString(16).padStart(2, "0")).join("").toUpperCase()}`;
  /* No recorte, a cor do fundo sai só da parte clara da borda (o produto que encosta na borda não escurece a média). */
  const fundo = brancos ? media(somaDosClaros, brancos) : opacos ? media(soma, opacos) : "#FFFFFF";
  if (transparentes / borda >= 0.55) return { tipo: "transparente", fundo };
  if ((brancos + transparentes) / borda >= 0.72) return { tipo: "recorte", fundo };
  return { tipo: "foto", fundo };
}

async function comoDataUri(imagem: sharp.Sharp) {
  const { data, info } = await imagem.webp({ quality: 90, alphaQuality: 95 }).toBuffer({ resolveWithObject: true });
  return { src: `data:image/webp;base64,${data.toString("base64")}`, largura: info.width, altura: info.height };
}

/** A imagem pronta para o HTML: sem a margem vazia (recorte e PNG sem fundo) e no tamanho que o banner usa. */
export async function imagemDoBanner(conteudo: Buffer, ladoMaximo: number): Promise<ImagemDoBanner> {
  const { tipo, fundo } = await analisarImagem(conteudo);
  let base = sharp(conteudo, { limitInputPixels: LIMITE_DE_PIXELS, animated: false }).rotate();
  if (tipo !== "foto") {
    try {
      const aparado = await base.clone().trim({ threshold: 18 }).toBuffer();
      base = sharp(aparado);
    } catch {
      /* Imagem toda de uma cor só: fica como está. */
    }
  }
  const pronta = await comoDataUri(base.resize(ladoMaximo, ladoMaximo, { fit: "inside", withoutEnlargement: true }));
  return { ...pronta, tipo, fundo };
}

/** O logo da loja, com a margem vazia aparada e no máximo 220 px de altura. */
export async function logoDoBanner(conteudo: Buffer): Promise<LogoDoBanner> {
  let base = sharp(conteudo, { limitInputPixels: LIMITE_DE_PIXELS, animated: false }).rotate();
  try {
    base = sharp(await base.clone().trim({ threshold: 10 }).toBuffer());
  } catch {
    /* sem margem para aparar */
  }
  return comoDataUri(base.resize(900, 220, { fit: "inside", withoutEnlargement: true }));
}

/* Loja: WebP (leve). Story e post: JPEG, que o Instagram e o WhatsApp aceitam. */
export async function arquivoFinal(png: Buffer, paraRedeSocial: boolean) {
  if (paraRedeSocial) {
    return { conteudo: await sharp(png).jpeg({ quality: 90, mozjpeg: true }).toBuffer(), tipo: "image/jpeg", extensao: "jpg" };
  }
  return { conteudo: await sharp(png).webp({ quality: 88 }).toBuffer(), tipo: "image/webp", extensao: "webp" };
}
