import { readFile } from 'node:fs/promises';
import path from 'node:path';
import sharp from 'sharp';
import type { Calidad } from '@/budget/types';
import { CALIDAD_LABEL } from '@/lib/funnel';

const MIME: Record<string, string> = {
  '.webp': 'image/webp',
  '.png': 'image/png',
  '.jpg': 'image/jpeg',
  '.jpeg': 'image/jpeg',
  '.svg': 'image/svg+xml',
};

// @react-pdf solo pinta PNG y JPEG. Cualquier otro formato (WebP de nuestros uploads y
// assets, SVG de los logos…) se rasteriza con sharp; si no, la imagen se descarta en
// silencio y el PDF sale sin ella. El render va a JPEG (foto, sin transparencia → ligero)
// y el logo a PNG (conserva transparencia).
const PASSTHROUGH = new Set(['image/png', 'image/jpeg']);

type OpcionesImagen = { formato?: 'png' | 'jpeg'; maxAncho?: number };

async function bufferADataUri(buf: Buffer, mime: string, opts: OpcionesImagen): Promise<string> {
  if (PASSTHROUGH.has(mime)) {
    return `data:${mime};base64,${buf.toString('base64')}`;
  }
  const pipe = sharp(buf, { density: 200 }).resize({
    width: opts.maxAncho ?? 1400,
    withoutEnlargement: true,
  });
  if (opts.formato === 'jpeg') {
    const jpeg = await pipe.flatten({ background: '#ffffff' }).jpeg({ quality: 80 }).toBuffer();
    return `data:image/jpeg;base64,${jpeg.toString('base64')}`;
  }
  const png = await pipe.png().toBuffer();
  return `data:image/png;base64,${png.toString('base64')}`;
}

// Convierte el origen de una imagen (renderUrl, logoUrl) en un data URI PNG/JPEG que
// @react-pdf pueda incrustar. Acepta data URIs, rutas relativas a /public y URLs http(s).
export async function resolverImagenPdf(
  url: string | null,
  opts: OpcionesImagen = {}
): Promise<string | null> {
  if (!url) return null;

  if (url.startsWith('data:')) {
    const coma = url.indexOf(',');
    if (coma === -1) return null;
    const cabecera = url.slice(5, coma);
    const mime = cabecera.split(';')[0] || 'application/octet-stream';
    const datos = url.slice(coma + 1);
    const buf = /;base64/i.test(cabecera)
      ? Buffer.from(datos, 'base64')
      : Buffer.from(decodeURIComponent(datos));
    try {
      return await bufferADataUri(buf, mime, opts);
    } catch {
      return null;
    }
  }

  // La pipeline de render entrega PNG/JPEG; las URLs remotas se pasan tal cual.
  if (url.startsWith('http://') || url.startsWith('https://')) return url;

  if (!url.startsWith('/')) return null;
  const publicDir = path.join(process.cwd(), 'public');
  const abs = path.normalize(path.join(publicDir, url));
  if (abs !== publicDir && !abs.startsWith(publicDir + path.sep)) return null;

  const mime = MIME[path.extname(abs).toLowerCase()];
  if (!mime) return null;
  try {
    return await bufferADataUri(await readFile(abs), mime, opts);
  } catch {
    return null;
  }
}

function listaNatural(items: string[]): string {
  if (items.length <= 1) return items[0] ?? '';
  return `${items.slice(0, -1).join(', ')} y ${items[items.length - 1]}`;
}

// Describe el render combinando los acabados (materialesRender del desglose) con el
// estilo detectado en la llamada. Si falta cualquiera de los dos, degrada con elegancia.
export function construirDescripcionRender(opts: {
  calidad: Calidad | null;
  materiales: string[];
  estilo: string[];
}): string {
  const calidad = CALIDAD_LABEL[opts.calidad ?? 'media'].toLowerCase();
  const materiales = opts.materiales.map((s) => s.trim()).filter(Boolean).slice(0, 4);
  const estilo = opts.estilo.map((s) => s.trim()).filter(Boolean).slice(0, 3);

  if (materiales.length === 0 && estilo.length === 0) {
    return `Recreación orientativa de cómo quedaría tu espacio reformado con la ${calidad} seleccionada.`;
  }

  const partes = [
    materiales.length > 0
      ? `Recreación con ${calidad} y acabados en ${listaNatural(materiales)}.`
      : `Recreación con ${calidad}.`,
  ];
  if (estilo.length > 0) {
    partes.push(`Estilo ${listaNatural(estilo)} según tus preferencias.`);
  }
  return partes.join(' ');
}
