import { NextResponse } from "next/server";
import sharp from "sharp";
import { createClient } from "@/lib/supabase-server";
import { canAnyCompany, getCompanyContext, resolveCompanyPublicProfileId } from "@/lib/company-context";
import { ChromeAusente } from "@/lib/catalogo-pdf/servidor/imprimir";
import { contatosNovos } from "@/lib/assistentes/contatos";
import { buscar } from "@/lib/assistentes/importacao/rede";
import { BannerDemorou, GeradorOcupado, imprimirImagem } from "@/lib/assistentes/ferramentas/gerador-de-banner";
import { arquivoFinal, imagemDoBanner, logoDoBanner } from "@/lib/assistentes/ferramentas/gerador-de-banner-imagem";
import {
  FONTE_PADRAO_DO_TEXTO,
  FONTE_PADRAO_DO_TITULO,
  coresDoBanner,
  disposicaoPelaProporcao,
  hexValido,
  htmlDoBanner,
  lerModelo,
  type ImagemDoBanner,
  type LogoDoBanner,
} from "@/lib/assistentes/ferramentas/gerador-de-banner-modelos";
import { chaveDaFonte } from "@/lib/assistentes/ferramentas/loja-e-documentos-aparencia";

export const runtime = "nodejs";
export const dynamic = "force-dynamic";
export const maxDuration = 90;

/**
 * "Gerar banner" do editor da página inicial: o mesmo gerador do assistente
 * (o HTML dos modelos e o Chrome da casa, um banner por vez na fila), no
 * tamanho exato da imagem da seção. A imagem pronta vai para o depósito
 * `products` da loja, com a sessão de quem pediu (o mesmo caminho do envio
 * de imagem das telas), e volta o endereço para o campo.
 *
 * Mesma permissão do assistente: criar banners (catalog.banners.write) ou
 * cuidar do marketing (marketing.manage). O texto passa pela mesma peneira
 * de contato (link, telefone, e-mail, Pix): banner gerado não leva contato.
 */

const UMA_LINHA = (valor: unknown, maximo: number) => String(valor ?? "").replace(/\s+/g, " ").trim().slice(0, maximo);

function erro(texto: string, status: number) {
  return NextResponse.json({ error: texto }, { status, headers: { "Cache-Control": "no-store" } });
}

async function baixarImagem(endereco: string): Promise<Buffer | null> {
  if (!/^https:\/\//i.test(endereco)) return null;
  try {
    const resposta = await buscar(endereco, { limite: 15 * 1024 * 1024, tempoMs: 20_000, aceita: "image/*" });
    if (resposta.status !== 200) return null;
    const meta = await sharp(resposta.corpo, { limitInputPixels: 60_000_000, animated: false }).metadata();
    if (!meta.format || meta.format === "svg") return null;
    return resposta.corpo;
  } catch {
    return null;
  }
}

export async function POST(req: Request) {
  const supabase = await createClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();
  if (!user) return erro("Entre no painel de novo.", 401);

  const ctx = await getCompanyContext(supabase, user);
  if (!canAnyCompany(ctx, user.id, ["catalog.banners.write", "marketing.manage"])) {
    return erro("Sem permissão para criar banners. Quem libera é o dono da empresa, em Funcionários.", 403);
  }

  const corpo = (await req.json().catch(() => null)) as Record<string, unknown> | null;
  if (!corpo) return erro("Pedido inválido.", 400);

  const titulo = UMA_LINHA(corpo.titulo, 60);
  const subtitulo = UMA_LINHA(corpo.subtitulo, 120);
  const botao = UMA_LINHA(corpo.botao, 24);
  const selo = UMA_LINHA(corpo.selo, 16);
  if (!titulo) return erro("Escreva o título do banner.", 400);
  const contatos = contatosNovos(null, [titulo, subtitulo, botao, selo].join("\n"));
  if (contatos.length) {
    return erro("O banner gerado não leva link, telefone, e-mail ou chave Pix. Tire esse dado do texto (ou envie uma arte pronta).", 400);
  }

  const modelo = lerModelo(corpo.modelo);
  if (!modelo) return erro("Escolha um dos modelos de desenho.", 400);

  const largura = Math.round(Number(corpo.largura));
  const altura = Math.round(Number(corpo.altura));
  if (!Number.isFinite(largura) || !Number.isFinite(altura) || largura < 320 || altura < 120 || largura > 2400 || altura > 2400) {
    return erro("Tamanho do banner inválido.", 400);
  }
  const { disposicao, formato } = disposicaoPelaProporcao(largura, altura);

  const cores: Record<"fundo" | "texto" | "destaque", string | null> = { fundo: null, texto: null, destaque: null };
  for (const campo of ["fundo", "texto", "destaque"] as const) {
    const bruto = (corpo.cores as Record<string, unknown> | undefined)?.[campo];
    if (bruto === undefined || bruto === null || bruto === "") continue;
    const cor = hexValido(bruto);
    if (!cor) return erro("Uma das cores não é válida.", 400);
    cores[campo] = cor;
  }

  const dono = resolveCompanyPublicProfileId(ctx, user.id);
  const { data: perfil } = await supabase
    .from("profiles")
    .select("primary_color,catalog_font_heading,catalog_font_body,catalog_logo_url,logo_url,catalog_name,store_name")
    .eq("id", dono)
    .maybeSingle();
  const loja = (perfil ?? {}) as Record<string, string | null>;

  /* A foto: de um produto da loja ou uma imagem que a pessoa enviou. */
  let enderecoDaFoto = "";
  const produtoId = String(corpo.produto_id ?? "").trim();
  if (produtoId) {
    if (!/^[0-9a-f-]{36}$/i.test(produtoId)) return erro("Produto inválido.", 400);
    const tenants = ctx.tenantIds?.length ? ctx.tenantIds : [user.id];
    const { data: produto } = await supabase.from("products").select("image_url").eq("id", produtoId).in("user_id", tenants).maybeSingle();
    enderecoDaFoto = String((produto as { image_url?: string | null } | null)?.image_url || "");
    if (!enderecoDaFoto) return erro("Esse produto não tem foto. Escolha outro ou envie uma imagem.", 400);
  } else if (corpo.imagem) {
    enderecoDaFoto = String(corpo.imagem).trim().slice(0, 2000);
  }
  let imagem: ImagemDoBanner | null = null;
  if (enderecoDaFoto) {
    const bytes = await baixarImagem(enderecoDaFoto);
    if (!bytes) return erro("Não deu para abrir essa foto. Tente outra (PNG, JPG ou WebP).", 400);
    try {
      imagem = await imagemDoBanner(bytes, disposicao === "faixa" ? 700 : 1920);
    } catch {
      return erro("Não deu para usar essa foto no banner. Tente outra.", 400);
    }
  }

  const usarLogo = corpo.usar_logo !== false;
  let logo: LogoDoBanner | null = null;
  const enderecoDoLogo = String(loja.catalog_logo_url || loja.logo_url || "");
  if (usarLogo && enderecoDoLogo) {
    const bytes = await baixarImagem(enderecoDoLogo);
    if (bytes) logo = await logoDoBanner(bytes).catch(() => null);
  }

  const paleta = coresDoBanner({ modelo, corDaLoja: loja.primary_color, fundo: cores.fundo, texto: cores.texto, destaque: cores.destaque });
  const html = htmlDoBanner({
    formato,
    medida: { largura, altura, disposicao },
    modelo,
    titulo,
    subtitulo,
    botao,
    selo,
    imagem,
    logo,
    nomeDaLoja: loja.catalog_name || loja.store_name || null,
    usarLogo,
    cores: paleta,
    fonteDoTitulo: chaveDaFonte(loja.catalog_font_heading) || FONTE_PADRAO_DO_TITULO,
    fonteDoTexto: chaveDaFonte(loja.catalog_font_body) || FONTE_PADRAO_DO_TEXTO,
  });

  let png: Buffer;
  try {
    png = await imprimirImagem(html, largura, altura);
  } catch (falha) {
    if (falha instanceof ChromeAusente) return erro("O gerador de banners não está disponível agora. Você ainda pode enviar uma imagem pronta.", 503);
    if (falha instanceof BannerDemorou) return erro("O banner demorou demais para ficar pronto. Tente de novo em instantes.", 504);
    if (falha instanceof GeradorOcupado) return erro("O gerador está ocupado com outros banners. Tente de novo em um minuto.", 429);
    console.error("[editor] gerar banner", falha instanceof Error ? falha.message : falha);
    return erro("Não deu para gerar o banner agora. Tente de novo em instantes.", 500);
  }

  const final = await arquivoFinal(png, false);
  const caminho = `catalog/sections/pagina-inicial/ia/${Math.random().toString(16).slice(2, 10)}_${Date.now()}.${final.extensao}`;
  const { error: erroDoEnvio } = await supabase.storage.from("products").upload(caminho, final.conteudo, { contentType: final.tipo, upsert: false });
  if (erroDoEnvio) {
    console.error("[editor] guardar banner", erroDoEnvio.message);
    return erro("O banner ficou pronto, mas não deu para guardar. Tente de novo.", 500);
  }
  const url = supabase.storage.from("products").getPublicUrl(caminho).data.publicUrl;
  return NextResponse.json({ url, largura, altura, modelo }, { headers: { "Cache-Control": "no-store" } });
}
