// LA PUERTA DE LA IMPRENTA DIGITAL.
//
// Para el negocio que YA tiene su sistema administrativo homologado y solo
// necesita quien le asigne el numero de control de sus ventas electronicas
// (000102, art. 5). Ese cliente no instala nada nuestro: arma su documento y
// nos lo manda con su llave.
//
// Lo que devolvemos: el numero de control, y la direccion del sello para que la
// impriman en su factura.
interface Entorno {
  SUPABASE_URL: string;
  SUPABASE_SERVICE_KEY: string;
  SELLO_BASE?: string;
}

interface Peticion {
  rif?: string;
  serie?: string;
  tipo?: string;
  numero_documento?: number;
  /** Sello del documento en el sistema del cliente: evita numerar dos veces lo mismo. */
  hash?: string;
  fecha?: string;
  base_bs?: number;
  iva_bs?: number;
  total_bs?: number;
  documento?: unknown;
  /** La cara del negocio para el PDF del correo (opcional, va aparte del documento). */
  marca?: { mensaje_pie?: string | null; logo_url?: string | null; color?: string | null } | null;
}

/**
 * Deja pasar SOLO los tres campos de la marca, recortados y con forma sana.
 * Lo que no cumpla, se bota en silencio: la marca es cosmetica, nunca debe
 * tumbar una emision.
 */
function marcaLimpia(m: Peticion['marca']): Record<string, string> | null {
  if (!m || typeof m !== 'object') return null;
  const limpia: Record<string, string> = {};
  if (typeof m.mensaje_pie === 'string' && m.mensaje_pie.trim()) limpia.mensaje_pie = m.mensaje_pie.trim().slice(0, 240);
  if (typeof m.logo_url === 'string' && /^https:\/\/\S+$/.test(m.logo_url.trim())) limpia.logo_url = m.logo_url.trim().slice(0, 500);
  if (typeof m.color === 'string' && /^#[0-9a-fA-F]{6}$/.test(m.color.trim())) limpia.color = m.color.trim();
  return Object.keys(limpia).length ? limpia : null;
}

const TIPOS = ['factura', 'nota_credito', 'nota_debito'];

function json(datos: unknown, estado = 200): Response {
  return new Response(JSON.stringify(datos), {
    status: estado,
    headers: { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' },
  });
}

/**
 * Anota un intento fallido. Va desde ACA y no desde la base a proposito: alla
 * el `raise exception` revierte la transaccion, y con ella se iria el registro
 * (ese era el bug que hacia decorativo el freno anti fuerza bruta).
 */
function anotarFallo(env: Entorno, rif: string, motivo: string, desde: string | null): Promise<unknown> {
  return fetch(`${env.SUPABASE_URL}/rest/v1/rpc/fn_registrar_intento`, {
    method: 'POST',
    headers: {
      apikey: env.SUPABASE_SERVICE_KEY,
      Authorization: `Bearer ${env.SUPABASE_SERVICE_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ p_quien: rif, p_motivo: motivo, p_desde: desde, p_tipo: 'estacion' }),
  }).catch(() => null);
}

export const onRequestPost: PagesFunction<Entorno> = async (ctx) => {
  // La llave viaja en la cabecera, nunca en la direccion: las direcciones
  // quedan escritas en registros y en historiales.
  const llave = (ctx.request.headers.get('authorization') ?? '').replace(/^Bearer\s+/i, '').trim();
  if (!llave) return json({ error: 'Falta la llave de acceso' }, 401);

  let p: Peticion;
  try {
    p = (await ctx.request.json()) as Peticion;
  } catch {
    return json({ error: 'El cuerpo no es un JSON valido' }, 400);
  }

  // Se revisa ANTES de tocar la base: un documento incompleto se rechaza aca
  // con un mensaje claro, que es lo que le sirve a quien esta integrando.
  const faltan: string[] = [];
  if (!p.rif) faltan.push('rif');
  if (!p.serie) faltan.push('serie');
  if (!p.tipo || !TIPOS.includes(p.tipo)) faltan.push('tipo (factura, nota_credito o nota_debito)');
  if (!Number.isFinite(p.numero_documento)) faltan.push('numero_documento');
  if (!p.hash) faltan.push('hash (identificador unico del documento en su sistema)');
  if (!p.fecha) faltan.push('fecha');
  if (!Number.isFinite(p.base_bs)) faltan.push('base_bs');
  if (!Number.isFinite(p.iva_bs)) faltan.push('iva_bs');
  if (!Number.isFinite(p.total_bs)) faltan.push('total_bs');
  if (!p.documento) faltan.push('documento');
  if (faltan.length) return json({ error: 'Faltan datos obligatorios', faltan }, 400);

  const r = await fetch(`${ctx.env.SUPABASE_URL}/rest/v1/rpc/fn_emitir_documento`, {
    method: 'POST',
    headers: {
      apikey: ctx.env.SUPABASE_SERVICE_KEY,
      Authorization: `Bearer ${ctx.env.SUPABASE_SERVICE_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      p_llave: llave,
      p_rif: p.rif,
      p_serie: p.serie,
      p_tipo: p.tipo,
      p_numero_documento: p.numero_documento,
      p_hash: p.hash,
      p_fecha: p.fecha,
      p_base_bs: p.base_bs,
      p_iva_bs: p.iva_bs,
      p_total_bs: p.total_bs,
      p_documento: p.documento,
      p_desde: ctx.request.headers.get('cf-connecting-ip') ?? null,
      p_marca: marcaLimpia(p.marca),
    }),
  });

  if (!r.ok) {
    const detalle = (await r.json().catch(() => ({}))) as { message?: string };
    const msg = detalle.message ?? '';
    // "No autorizado" se responde tal cual, sin decir si fallo el RIF o la
    // llave: a quien esta probando no se le regala informacion.
    if (/no autorizado/i.test(msg)) {
      ctx.waitUntil(anotarFallo(ctx.env, p.rif as string, 'emision', ctx.request.headers.get('cf-connecting-ip')));
      return json({ error: 'No autorizado' }, 401);
    }
    if (/demasiados intentos/i.test(msg)) return json({ error: 'Demasiados intentos fallidos' }, 429);
    return json({ error: msg || 'No se pudo emitir el documento' }, 400);
  }

  const filas = (await r.json()) as {
    numero_control: number;
    token: string;
    ya_estaba: boolean;
    fecha_asignacion?: string | null;
    rango_desde?: number | null;
    rango_hasta?: number | null;
    imprenta_rif?: string | null;
    imprenta_razon_social?: string | null;
    imprenta_providencia?: string | null;
    imprenta_providencia_fecha?: string | null;
  }[];
  const d = filas?.[0];
  if (!d) return json({ error: 'No se pudo emitir el documento' }, 500);

  const base = ctx.env.SELLO_BASE ?? 'https://sello.gustitoxpress.app';
  return json({
    numero_control: d.numero_control,
    // La forma impresa que exige el art. 30 de la 000102: identificador de dos
    // digitos mas un secuencial de hasta ocho ("00-00001234"). El identificador
    // 00 es el lote inicial de esta imprenta; cuando el secuencial pase de ocho
    // digitos se abre el lote 01.
    numero_control_texto: `${String(Math.floor(d.numero_control / 100000000)).padStart(2, '0')}-${String(d.numero_control % 100000000).padStart(8, '0')}`,
    // La direccion que el cliente imprime como QR en su factura.
    sello: `${base}/v/${d.token}`,
    // Avisa si ese documento ya se habia numerado antes (reintento del cliente).
    ya_estaba: d.ya_estaba,
    // Lo que el articulo 7 de la Providencia SNAT/2024/000102 obliga a imprimir
    // en la factura y solo la imprenta conoce: la fecha de asignacion del
    // numero de control (numeral 15), el rango del que salio ese numero
    // (numeral 5, vacio mientras el emisor trabaja con numeracion libre) y la
    // identidad de la imprenta con su providencia (numeral 14, vacia hasta que
    // la imprenta registre su autorizacion en el panel).
    fecha_asignacion: d.fecha_asignacion ?? null,
    rango: d.rango_desde != null && d.rango_hasta != null
      ? { desde: d.rango_desde, hasta: d.rango_hasta }
      : null,
    imprenta: d.imprenta_rif && d.imprenta_razon_social
      ? {
          rif: d.imprenta_rif,
          razon_social: d.imprenta_razon_social,
          providencia: d.imprenta_providencia ?? null,
          providencia_fecha: d.imprenta_providencia_fecha ?? null,
        }
      : null,
  });
};
