/**
 * Cotizador Walmart Connect · Google Workspace Worker
 * GomezLee Marketing · 2026
 *
 * Versión corregida para Google Apps Script V8.
 * - NO usa variables de entorno.
 * - NO usa Script Properties.
 * - Gemini NO se llama desde Apps Script.
 * - Las imágenes llegan desde los nodos nativos de Google Gemini en n8n.
 */

const WMC_CONFIG = {
  GOOGLE_AUTOMATION_SECRET: 'mwI_n-1r4_-QTLp1-zIt-zQKh_HqaoGoVhyapyLN2T2-r7xf8iP5AC3v9wJt5Av6',
  OUTPUT_FOLDER_ID: '19EmZsJ7PzWCaFzeij9AZ3-mkPctNyK0G',
  WALMART_TEMPLATE_ID: '17f1MB1uXQiQk4jn4_MPQixpBRwCpwDMZTsRmWA9UMOA',
  GLM_MONTHLY_TEMPLATE_ID: '1RL9BckErp1QCLdxlM6riBRh9iO4f14ezf6fUgizBZJw',

  // Confirmado en los ejemplos GLM enviados (Guatemala y Costa Rica).
  GLM_VENDOR_NUMBER: '1603060802',
  GLM_IVA_RATE: 0.12
};

const WMC = {
  NAVY: '#041E42',
  GREEN: '#6CC24A',
  BLUE: '#4F758B',
  TEXT: '#4A4A4A',
  LIGHT: '#F5F5F5'
};

/**
 * Health check sencillo: al abrir la URL /exec en el navegador debe devolver JSON.
 */
function doGet() {
  return json_({
    ok: true,
    service: 'Cotizador WMC · Google Workspace Worker',
    version: '2026-08-23.3'
  });
}

function doPost(e) {
  try {
    const body = JSON.parse((e && e.postData && e.postData.contents) || '{}');
    requireSecret_(body.secret);

    const action = String(body.action || '');
    let result;

    if (action === 'generate_quote') {
      result = generateWalmartQuote_(body);
    } else if (action === 'generate_monthly_glm') {
      result = generateMonthlyGlm_(body);
    } else if (action === 'generate_proposal') {
      result = generateProposal_(body);
    } else if (action === 'generate_image') {
      result = generateImageArtifact_(body);
    } else {
      result = { ok: true, skipped: true, message: 'Acción sin artefacto.' };
    }

    return json_(result);
  } catch (err) {
    console.error(err && err.stack ? err.stack : err);
    return json_({
      ok: false,
      error: String(err && err.message ? err.message : err)
    });
  }
}

function requireSecret_(value) {
  if (!WMC_CONFIG.GOOGLE_AUTOMATION_SECRET || String(value || '') !== WMC_CONFIG.GOOGLE_AUTOMATION_SECRET) {
    throw new Error('Solicitud no autorizada.');
  }
}

function json_(obj) {
  return ContentService
    .createTextOutput(JSON.stringify(obj))
    .setMimeType(ContentService.MimeType.JSON);
}

function safeName_(value, fallback) {
  const s = String(value || fallback || 'WMC')
    .replace(/[\\/:*?"<>|#%{}\[\]]+/g, ' ')
    .replace(/\s+/g, ' ')
    .trim();
  return s.slice(0, 160) || String(fallback || 'WMC');
}

function outputFolder_() {
  return DriveApp.getFolderById(WMC_CONFIG.OUTPUT_FOLDER_ID);
}

function copyTemplate_(templateId, name) {
  const file = DriveApp.getFileById(templateId);
  return file.makeCopy(safeName_(name, 'WMC'), outputFolder_());
}

function norm_(v) {
  return String(v || '')
    .normalize('NFD')
    .replace(/[\u0300-\u036f]/g, '')
    .toLowerCase()
    .replace(/\s+/g, ' ')
    .trim();
}

function findCell_(sheet, variants) {
  const values = sheet.getDataRange().getDisplayValues();
  const wanted = variants.map(function(v) { return norm_(v); });

  for (let r = 0; r < values.length; r++) {
    for (let c = 0; c < values[r].length; c++) {
      const n = norm_(values[r][c]);
      for (let i = 0; i < wanted.length; i++) {
        const w = wanted[i];
        if (n === w || n.indexOf(w + ':') === 0 || n.indexOf(w) >= 0) {
          return { row: r + 1, col: c + 1, text: values[r][c] };
        }
      }
    }
  }
  return null;
}

function findRow_(sheet, variants) {
  const c = findCell_(sheet, variants);
  return c ? c.row : null;
}

function setRightOfLabel_(sheet, variants, value) {
  if (value === undefined || value === null || value === '') return false;
  const cell = findCell_(sheet, variants);
  if (!cell) return false;
  sheet.getRange(cell.row, Math.min(cell.col + 1, sheet.getMaxColumns())).setValue(value);
  return true;
}

function visibleFirstSheet_(ss) {
  const sheets = ss.getSheets();
  for (let i = 0; i < sheets.length; i++) {
    if (!sheets[i].isSheetHidden()) return sheets[i];
  }
  return sheets[0];
}

function findExactTextRowInColumnAfter_(sheet, col, text, afterRow, beforeRow) {
  const start = Math.max(1, Number(afterRow || 1));
  const end = Math.min(sheet.getMaxRows(), Number(beforeRow || sheet.getMaxRows()));
  if (end < start) return null;

  const values = sheet.getRange(start, col, end - start + 1, 1).getDisplayValues();
  const wanted = norm_(text);

  for (let i = 0; i < values.length; i++) {
    if (norm_(values[i][0]) === wanted) return start + i;
  }
  return null;
}

function firstNumber_() {
  for (let i = 0; i < arguments.length; i++) {
    const v = arguments[i];
    if (v === '' || v === null || v === undefined) continue;
    const n = Number(v);
    if (Number.isFinite(n)) return n;
  }
  return null;
}

function numOrBlank_(v) {
  if (v === '' || v === null || v === undefined) return '';
  const n = Number(v);
  return Number.isFinite(n) ? n : '';
}

function countryCurrencyCode_(code) {
  const map = { GT: 'GTQ', CR: 'CRC', SV: 'USD', HN: 'HNL', NI: 'NIO' };
  return map[String(code || '').toUpperCase()] || '';
}

function countryCurrencySymbol_(code) {
  const map = { GT: 'Q', CR: '₡', SV: '$', HN: 'L', NI: 'C$' };
  return map[String(code || '').toUpperCase()] || '';
}

// ============================================================================
// NORMALIZACIÓN FINANCIERA DEFENSIVA
// ============================================================================

function money_(value) {
  const n = Number(value);
  if (!Number.isFinite(n)) return 0;
  return Math.round((n + Number.EPSILON) * 100) / 100;
}

function isFeeSection_(section) {
  return norm_(section).indexOf('fee') >= 0;
}

function normalizeWalmartQuoteFinancials_(input) {
  const q = JSON.parse(JSON.stringify(input || {}));
  const rateRaw = Number(q.exchangeRate !== undefined ? q.exchangeRate : q.exchange_rate);
  const rate = Number.isFinite(rateRaw) && rateRaw > 0 ? rateRaw : 1;
  const items = Array.isArray(q.items) ? q.items : [];

  let subtotalUsd = 0;
  let explicitFeeUsd = 0;

  q.items = items.map(function(raw) {
    const it = raw && typeof raw === 'object' ? raw : {};
    const units = firstNumber_(it.units, it.quantity, 0);
    const days = (it.days === '' || it.days === null || it.days === undefined)
      ? 1
      : firstNumber_(it.days, 1);
    const unitUsd = firstNumber_(it.unitCostUsd, it.unit_cost_usd, 0);
    const suppliedTotal = firstNumber_(it.totalUsd, it.total_usd, 0);
    const computedTotal = money_(Number(units || 0) * Number(days || 0) * Number(unitUsd || 0));
    const totalUsd = Number(unitUsd || 0) > 0 && Number(units || 0) > 0
      ? computedTotal
      : money_(suppliedTotal || 0);

    const normalized = Object.assign({}, it, {
      units: Number(units || 0),
      days: Number(days || 0),
      unitCostUsd: money_(unitUsd || 0),
      totalUsd: totalUsd,
      unitCostLocal: money_(Number(unitUsd || 0) * rate),
      totalLocal: money_(totalUsd * rate)
    });

    if (isFeeSection_(it.section)) explicitFeeUsd += totalUsd;
    else subtotalUsd += totalUsd;

    return normalized;
  });

  subtotalUsd = money_(subtotalUsd);
  let feeRate = firstNumber_(q.agencyFeeRate, q.feeRate);
  if (feeRate === null && explicitFeeUsd > 0 && subtotalUsd > 0) feeRate = explicitFeeUsd / subtotalUsd;
  if (feeRate === null || !Number.isFinite(feeRate) || feeRate < 0) feeRate = 0.06;
  if (feeRate > 1) feeRate = feeRate / 100;

  const feeUsd = money_(subtotalUsd * feeRate);
  q.exchangeRate = rate;
  q.agencyFeeRate = feeRate;
  q.subtotalUsd = subtotalUsd;
  q.subtotalLocal = money_(subtotalUsd * rate);
  q.agencyFeeUsd = feeUsd;
  q.agencyFeeLocal = money_(feeUsd * rate);
  q.totalUsd = money_(subtotalUsd + feeUsd);
  q.totalLocal = money_(q.totalUsd * rate);

  return q;
}

function readWalmartGrandTotals_(sheet, q) {
  const totalCell = findCell_(sheet, ['Total con todos los rubros', 'Total general']);
  if (!totalCell) {
    return {
      totalUsd: money_(q.totalUsd || 0),
      totalLocal: money_(q.totalLocal || 0)
    };
  }

  const row = totalCell.row;
  const usd = Number(sheet.getRange(row, 5).getValue());
  const local = Number(sheet.getRange(row, 7).getValue());
  return {
    totalUsd: Number.isFinite(usd) ? money_(usd) : money_(q.totalUsd || 0),
    totalLocal: Number.isFinite(local) ? money_(local) : money_(q.totalLocal || 0)
  };
}

// ============================================================================
// 1) COTIZACIÓN OFICIAL WALMART CONNECT
// ============================================================================

function generateWalmartQuote_(body) {
  const q = normalizeWalmartQuoteFinancials_(body.quote || {});
  if (!q.countryCode || !Array.isArray(q.items) || !q.items.length) {
    throw new Error('La cotización no contiene país/items suficientes.');
  }

  const stamp = Utilities.formatDate(
    new Date(),
    Session.getScriptTimeZone() || 'America/Guatemala',
    'yyyyMMdd-HHmm'
  );

  const name = safeName_(
    'WMC_' + (q.project || q.advertiser || 'Cotizacion') + '_' + q.countryCode + '_' + stamp,
    'WMC_Cotizacion'
  );

  const file = copyTemplate_(WMC_CONFIG.WALMART_TEMPLATE_ID, name);
  const ss = SpreadsheetApp.openById(file.getId());
  const sheet = ss.getSheetByName('Solicitud Cotizaciones') || visibleFirstSheet_(ss);

  setRightOfLabel_(sheet, ['Brand Solution'], q.brandSolution || 'WMC');
  setRightOfLabel_(sheet, ['País', 'Pais'], q.country || q.countryCode);
  setRightOfLabel_(sheet, ['Agencia'], 'GomezLee Marketing S.A.');
  setRightOfLabel_(sheet, ['Proyecto'], q.project || '');
  setRightOfLabel_(sheet, ['Anunciante'], q.advertiser || '');
  setRightOfLabel_(sheet, ['Formato'], q.format || '');
  setRightOfLabel_(sheet, ['Tipo de cambio'], Number(q.exchangeRate || 1));

  const sectionDefs = [
    { key: 'Personal', aliases: ['Personal'] },
    { key: 'Mobiliario - Equipo', aliases: ['Mobiliario - Equipo', 'Mobiliario y Equipo', 'Mobiliario'] },
    { key: 'Insumos de degustación y/o preparación', aliases: ['Insumos de degustación y/o preparación', 'Insumos de degustación', 'Insumos'] },
    { key: 'Logística e Instalación', aliases: ['Logística e Instalación', 'Logistica e Instalación', 'Logistica e Instalacion', 'Logística', 'Logistica'] }
  ];

  const grouped = {};
  for (let i = 0; i < q.items.length; i++) {
    const it = q.items[i] || {};
    const section = String(it.section || 'Otros');
    if (!grouped[section]) grouped[section] = [];
    grouped[section].push(it);
  }

  // De abajo hacia arriba para que las inserciones no desplacen secciones todavía no procesadas.
  const results = [];
  for (let i = sectionDefs.length - 1; i >= 0; i--) {
    const def = sectionDefs[i];
    const items = getItemsForSection_(q.items, def.aliases);
    results.push(fillWalmartSection_(sheet, def.aliases, items));
  }

  // Reordenamos para cálculos posteriores.
  const sectionTotals = [];
  for (let i = 0; i < sectionDefs.length; i++) {
    const headerRow = findRow_(sheet, sectionDefs[i].aliases);
    if (!headerRow) continue;
    const totalRow = findExactTextRowInColumnAfter_(sheet, 1, 'Total', headerRow + 1);
    if (totalRow) sectionTotals.push(totalRow);
  }

  configureWalmartFee_(sheet, q, sectionTotals);
  configureWalmartGrandTotal_(sheet, sectionTotals);

  setRightOfLabel_(sheet, ['Vigencia de la cotización', 'Vigencia'], q.validity || '30 días');
  setRightOfLabel_(
    sheet,
    ['Observaciones o exclusiones', 'Observaciones'],
    Array.isArray(q.observations) ? q.observations.join('\n') : (q.observations || '')
  );

  insertVisualReferences_(sheet, q.visualReferences || []);

  // Forzamos el recálculo del machote y guardamos en la trazabilidad los mismos
  // totales finales que ve el usuario en la hoja (incluyendo Fee de agencia).
  SpreadsheetApp.flush();
  const finalTotals = readWalmartGrandTotals_(sheet, q);
  q.totalUsd = finalTotals.totalUsd;
  q.totalLocal = finalTotals.totalLocal;

  let data = ss.getSheetByName('_WMC_DATA');
  if (!data) data = ss.insertSheet('_WMC_DATA');
  data.clear();
  data.getRange('A1:B8').setValues([
    ['generatedAt', new Date()],
    ['countryCode', q.countryCode],
    ['currencyCode', q.currencyCode || countryCurrencyCode_(q.countryCode)],
    ['exchangeRate', Number(q.exchangeRate || 1)],
    ['totalUsd', Number(q.totalUsd || 0)],
    ['totalLocal', Number(q.totalLocal || 0)],
    ['conversationId', body.conversationId || ''],
    ['quoteJson', JSON.stringify(q)]
  ]);
  data.hideSheet();

  try {
    sheet.setName(safeName_('Cotización ' + (q.format || '') + ' ' + q.countryCode, 'Cotización').slice(0, 99));
  } catch (ignore) {}

  SpreadsheetApp.flush();

  return {
    ok: true,
    artifactType: 'quote',
    artifactId: file.getId(),
    artifactUrl: ss.getUrl(),
    title: name,
    provider: 'Google Sheets'
  };
}

function getItemsForSection_(items, aliases) {
  const wanted = aliases.map(function(v) { return norm_(v); });
  const out = [];

  for (let i = 0; i < items.length; i++) {
    const it = items[i] || {};
    const section = norm_(it.section || '');
    let match = false;

    for (let j = 0; j < wanted.length; j++) {
      if (section === wanted[j] || section.indexOf(wanted[j]) >= 0 || wanted[j].indexOf(section) >= 0) {
        match = true;
        break;
      }
    }

    if (match) out.push(it);
  }
  return out;
}

function fillWalmartSection_(sheet, aliases, items) {
  const headerRow = findRow_(sheet, aliases);
  if (!headerRow) return { headerRow: null, totalRow: null };

  let totalRow = findExactTextRowInColumnAfter_(sheet, 1, 'Total', headerRow + 1);
  if (!totalRow) throw new Error('No se encontró la fila Total para la sección ' + aliases[0] + '.');

  const start = headerRow + 1;
  let slots = totalRow - start;
  const needed = Math.max(1, items.length);

  if (needed > slots) {
    const extra = needed - slots;
    sheet.insertRowsBefore(totalRow, extra);

    if (slots > 0) {
      sheet.getRange(start, 1, 1, 7).copyTo(
        sheet.getRange(totalRow, 1, extra, 7),
        SpreadsheetApp.CopyPasteType.PASTE_FORMAT,
        false
      );
    }

    totalRow += extra;
    slots += extra;
  }

  // Limpia únicamente las filas de detalle; nunca toca la fila Total.
  if (slots > 0) sheet.getRange(start, 1, slots, 7).clearContent();

  for (let i = 0; i < items.length; i++) {
    const row = start + i;
    const it = items[i] || {};

    sheet.getRange(row, 1, 1, 4).setValues([[
      it.description || 'Item',
      numOrBlank_(it.units),
      numOrBlank_(it.days),
      numOrBlank_(it.unitCostUsd)
    ]]);

    sheet.getRange(row, 5).setFormula('=B' + row + '*C' + row + '*D' + row);
    sheet.getRange(row, 6).setFormula('=D' + row + '*$B$11');
    sheet.getRange(row, 7).setFormula('=E' + row + '*$B$11');
  }

  // Mantiene fórmulas neutras en espacios libres para que el subtotal siga siendo confiable.
  for (let r = start + items.length; r < totalRow; r++) {
    sheet.getRange(r, 5).setFormula('=B' + r + '*C' + r + '*D' + r);
    sheet.getRange(r, 6).setFormula('=D' + r + '*$B$11');
    sheet.getRange(r, 7).setFormula('=E' + r + '*$B$11');
  }

  const end = totalRow - 1;
  sheet.getRange(totalRow, 4).setFormula('=SUM(D' + start + ':D' + end + ')');
  sheet.getRange(totalRow, 5).setFormula('=SUM(E' + start + ':E' + end + ')');
  sheet.getRange(totalRow, 6).setFormula('=SUM(F' + start + ':F' + end + ')');
  sheet.getRange(totalRow, 7).setFormula('=SUM(G' + start + ':G' + end + ')');

  sheet.getRange(start, 4, Math.max(1, slots), 2).setNumberFormat('$#,##0.00;[Red]-$#,##0.00');
  sheet.getRange(start, 6, Math.max(1, slots), 2).setNumberFormat('#,##0.00;[Red]-#,##0.00');

  return { headerRow: headerRow, totalRow: totalRow };
}

function configureWalmartFee_(sheet, q, sectionTotalRows) {
  const feeHeader = findRow_(sheet, ['Fee de agencia', 'Fee Agencia', 'Fee']);
  if (!feeHeader) return;

  const feeCalcRow = feeHeader + 1;
  let feeRate = firstNumber_(q.agencyFeeRate, q.feeRate);

  const feeItems = getItemsForSection_(q.items || [], ['Fee de agencia', 'Fee Agencia', 'Fee']);
  if (feeRate === null && feeItems.length) {
    const baseUsd = sumItemTotals_(q.items || [], false);
    const feeUsd = sumItemTotals_(feeItems, true);
    if (baseUsd > 0 && feeUsd >= 0) feeRate = feeUsd / baseUsd;
  }

  // Si Gemini no suministra un fee explícito, conserva el 6% del machote oficial.
  if (feeRate === null || !Number.isFinite(feeRate) || feeRate < 0) {
    const current = Number(sheet.getRange(feeHeader, 4).getValue());
    feeRate = Number.isFinite(current) && current >= 0 ? current : 0.06;
  }

  if (feeRate > 1) feeRate = feeRate / 100;

  sheet.getRange(feeHeader, 4).setValue(feeRate).setNumberFormat('0.00%');
  sheet.getRange(feeHeader, 6).setValue(feeRate).setNumberFormat('0.00%');

  const dRefs = sectionTotalRows.map(function(r) { return 'D' + r; });
  const eRefs = sectionTotalRows.map(function(r) { return 'E' + r; });
  const fRefs = sectionTotalRows.map(function(r) { return 'F' + r; });
  const gRefs = sectionTotalRows.map(function(r) { return 'G' + r; });

  sheet.getRange(feeCalcRow, 4).setFormula('=(' + dRefs.join('+') + ')*D' + feeHeader);
  sheet.getRange(feeCalcRow, 5).setFormula('=(' + eRefs.join('+') + ')*D' + feeHeader);
  sheet.getRange(feeCalcRow, 6).setFormula('=(' + fRefs.join('+') + ')*F' + feeHeader);
  sheet.getRange(feeCalcRow, 7).setFormula('=(' + gRefs.join('+') + ')*F' + feeHeader);
}

function configureWalmartGrandTotal_(sheet, sectionTotalRows) {
  const totalCell = findCell_(sheet, ['Total con todos los rubros', 'Total general']);
  if (!totalCell) return;

  const grandRow = totalCell.row;
  const feeHeader = findRow_(sheet, ['Fee de agencia', 'Fee Agencia', 'Fee']);
  const feeCalcRow = feeHeader ? feeHeader + 1 : null;

  const cols = [4, 5, 6, 7];
  for (let i = 0; i < cols.length; i++) {
    const col = cols[i];
    const letter = columnLetter_(col);
    const refs = sectionTotalRows.map(function(r) { return letter + r; });
    if (feeCalcRow) refs.push(letter + feeCalcRow);
    sheet.getRange(grandRow, col).setFormula('=' + refs.join('+'));
  }
}

function sumItemTotals_(items, includeAll) {
  let total = 0;
  for (let i = 0; i < items.length; i++) {
    const it = items[i] || {};
    if (!includeAll && norm_(it.section || '').indexOf('fee') >= 0) continue;
    const n = firstNumber_(it.totalUsd, it.total_usd);
    if (n !== null) total += n;
  }
  return total;
}

function columnLetter_(col) {
  let n = Number(col);
  let s = '';
  while (n > 0) {
    const rem = (n - 1) % 26;
    s = String.fromCharCode(65 + rem) + s;
    n = Math.floor((n - 1) / 26);
  }
  return s;
}

// ============================================================================
// 2) REFERENCIAS VISUALES
// ============================================================================

function insertVisualReferences_(sheet, refs) {
  if (!Array.isArray(refs) || !refs.length) return;

  const row = findRow_(sheet, ['Referencias visuales', 'Referencia visual']);
  if (!row) return;

  let col = 1;
  refs.slice(0, 6).forEach(function(ref) {
    try {
      const blob = visualBlob_(ref);
      if (!blob) return;

      const img = sheet.insertImage(blob, col, row + 1);
      const maxW = 360;
      const maxH = 260;
      const scale = Math.min(1, maxW / img.getWidth(), maxH / img.getHeight());

      img.setWidth(Math.round(img.getWidth() * scale));
      img.setHeight(Math.round(img.getHeight() * scale));
      col = Math.min(col + 4, Math.max(1, sheet.getMaxColumns() - 1));
    } catch (err) {
      console.warn('No se pudo insertar referencia visual: ' + err);
    }
  });
}

function driveIdFromUrl_(url) {
  const text = String(url || '');
  let m = text.match(/\/d\/([a-zA-Z0-9_-]{15,})/);
  if (m) return m[1];
  m = text.match(/[?&]id=([a-zA-Z0-9_-]{15,})/);
  return m ? m[1] : '';
}

function visualBlob_(ref) {
  const obj = ref && typeof ref === 'object' ? ref : {};
  const directId = String(obj.imageDriveId || obj.driveId || obj.fileId || '').trim();
  if (directId) return DriveApp.getFileById(directId).getBlob();

  const url = typeof ref === 'string' ? ref : String(obj.url || obj.imageUrl || '').trim();
  if (!url) return null;

  const driveId = driveIdFromUrl_(url);
  if (driveId) return DriveApp.getFileById(driveId).getBlob();

  const response = UrlFetchApp.fetch(url, {
    muteHttpExceptions: true,
    followRedirects: true
  });

  if (response.getResponseCode() < 200 || response.getResponseCode() >= 300) {
    throw new Error('No se pudo descargar la referencia visual. HTTP ' + response.getResponseCode());
  }
  return response.getBlob();
}

// ============================================================================
// 3) MENSUALIZACIÓN INTERNA GLM
// ============================================================================

function generateMonthlyGlm_(body) {
  const q = body.quote || {};
  const a = body.artifact || {};
  const metadata = a.metadata && typeof a.metadata === 'object' ? a.metadata : {};

  const stamp = Utilities.formatDate(
    new Date(),
    Session.getScriptTimeZone() || 'America/Guatemala',
    'yyyyMMdd-HHmm'
  );

  const name = safeName_(
    'GLM-' + (q.project || a.title || 'WMC') + '_' + (q.countryCode || '') + '_' + stamp,
    'GLM-WMC'
  );

  const file = copyTemplate_(WMC_CONFIG.GLM_MONTHLY_TEMPLATE_ID, name);
  const ss = SpreadsheetApp.openById(file.getId());
  const sheet = ss.getSheetByName('Cotización') || visibleFirstSheet_(ss);

  const country = q.country || q.countryName || q.countryCode || '';
  const countryCode = String(q.countryCode || '').toUpperCase();
  const advertiser = q.advertiser || a.client || '';
  const project = q.project || a.project || q.format || 'Cotización Walmart Connect';
  const contact = a.attention || metadata.attention || q.contactName || q.contact_name || '';
  const email = a.email || metadata.email || q.contactEmail || q.contact_email || (body.user ? body.user.email : '') || '';
  const currencySymbol = a.currencySymbol || metadata.currencySymbol || q.currencySymbol || countryCurrencySymbol_(countryCode);
  const vendor = a.vendorNumber || metadata.vendorNumber || WMC_CONFIG.GLM_VENDOR_NUMBER;
  const quoteCode = a.quoteCode || metadata.quoteCode || q.quoteCode || q.quote_code || '';

  // El formato GLM real guarda estos valores dentro de la misma celda A6:A12.
  sheet.getRange('A6').setValue('País: ' + country);
  sheet.getRange('E6').setValue(quoteCode);
  sheet.getRange('A7').setValue('Empresa: Walmart Connect');
  sheet.getRange('A8').setValue('Asunto: ' + project);
  sheet.getRange('A9').setValue('Atención:' + contact);
  sheet.getRange('A10').setValue('Correo:' + email);
  sheet.getRange('A11').setValue('Moneda: ' + currencySymbol);
  sheet.getRange('A12').setValue('Vendor #: ' + vendor);

  const summaryTitle = a.summaryTitle || metadata.summaryTitle || ('Walmart Connect-' + (advertiser || project));
  sheet.getRange('A14').setValue(summaryTitle);

  const monthlyDate = parseDateOrNow_(a.monthlyDate || metadata.monthlyDate || a.date || metadata.date);
  sheet.getRange('A15').setValue(monthlyDate).setNumberFormat('mmmm yyyy');
  sheet.getRange('B17').setValue(country);

  const lines = Array.isArray(a.monthlyLines) && a.monthlyLines.length
    ? a.monthlyLines
    : (Array.isArray(q.items) ? q.items : []);

  const activity = fillGlmActivityLines_(sheet, lines, q, a, metadata);
  refreshGlmBudgetSummary_(sheet, activity);

  let data = ss.getSheetByName('_WMC_MONTHLY_DATA');
  if (!data) data = ss.insertSheet('_WMC_MONTHLY_DATA');
  data.clear();
  data.getRange('A1:B7').setValues([
    ['generatedAt', new Date()],
    ['countryCode', countryCode],
    ['currency', currencySymbol],
    ['sourceQuoteId', a.sourceQuoteId || ''],
    ['conversationId', body.conversationId || ''],
    ['quoteJson', JSON.stringify(q)],
    ['artifactJson', JSON.stringify(a)]
  ]);
  data.hideSheet();

  SpreadsheetApp.flush();

  return {
    ok: true,
    artifactType: 'monthly_glm',
    artifactId: file.getId(),
    artifactUrl: ss.getUrl(),
    title: name,
    provider: 'Google Sheets'
  };
}

function fillGlmActivityLines_(sheet, lines, quote, artifact, metadata) {
  const header = findCell_(sheet, ['DESCRIPCIÓN', 'Descripcion', 'Descripción']);
  if (!header) throw new Error('No se encontró el encabezado DESCRIPCIÓN en la plantilla GLM.');

  const start = header.row + 1;
  let totalRow = findExactTextRowInColumnAfter_(sheet, 4, 'Total', start);
  if (!totalRow) throw new Error('No se encontró la primera fila Total del bloque de actividades GLM.');

  let slots = totalRow - start;
  const needed = Math.max(1, lines.length);

  if (needed > slots) {
    const extra = needed - slots;
    sheet.insertRowsBefore(totalRow, extra);

    if (slots > 0) {
      sheet.getRange(start, 1, 1, 5).copyTo(
        sheet.getRange(totalRow, 1, extra, 5),
        SpreadsheetApp.CopyPasteType.PASTE_FORMAT,
        false
      );
    }

    totalRow += extra;
    slots += extra;
  }

  if (slots > 0) sheet.getRange(start, 1, slots, 5).clearContent();

  for (let i = 0; i < lines.length; i++) {
    const row = start + i;
    const it = lines[i] || {};
    const qty = firstNumber_(it.units, it.quantity, 1);
    const days = firstNumber_(it.days, 1);
    const localUnit = firstNumber_(
      it.unitCostLocal,
      it.unit_cost_local,
      firstNumber_(it.unitCostUsd, it.unit_cost_usd, 0) * firstNumber_(quote.exchangeRate, quote.exchange_rate, 1)
    );

    sheet.getRange(row, 1, 1, 4).setValues([[
      it.description || it.concept || it.item_name || 'Item',
      qty,
      days,
      localUnit
    ]]);
    sheet.getRange(row, 5).setFormula('=D' + row + '*B' + row + '*C' + row);
  }

  const end = totalRow - 1;
  sheet.getRange(totalRow, 4).setValue('Total ');
  sheet.getRange(totalRow, 5).setFormula('=SUM(E' + start + ':E' + end + ')');

  const taxRow = totalRow + 1;
  const grandRow = totalRow + 2;
  let taxRate = firstNumber_(artifact.taxRate, metadata.taxRate, WMC_CONFIG.GLM_IVA_RATE);
  if (taxRate > 1) taxRate = taxRate / 100;

  sheet.getRange(taxRow, 4).setValue('IVA ');
  sheet.getRange(taxRow, 5).setFormula('=E' + totalRow + '*' + taxRate);
  sheet.getRange(grandRow, 4).setValue('Total ');
  sheet.getRange(grandRow, 5).setFormula('=E' + totalRow + '+E' + taxRow);

  sheet.getRange(start, 4, Math.max(1, slots), 2).setNumberFormat('#,##0.00;[Red]-#,##0.00');
  sheet.getRange(totalRow, 5, 3, 1).setNumberFormat('#,##0.00;[Red]-#,##0.00');

  return {
    startRow: start,
    endRow: end,
    totalRow: totalRow,
    taxRow: taxRow,
    grandRow: grandRow
  };
}

function refreshGlmBudgetSummary_(sheet, activity) {
  const pmCell = findCell_(sheet, ['Project Management']);
  let pmTotalRow = null;

  if (pmCell) {
    // En el machote GLM real, el total de Project Management es el último Total antes de Presupuesto.
    const presupuestoCell = findCell_(sheet, ['Presupuesto']);
    const before = presupuestoCell ? presupuestoCell.row - 1 : sheet.getMaxRows();

    for (let r = before; r > pmCell.row; r--) {
      const d = norm_(sheet.getRange(r, 4).getDisplayValue());
      if (d === 'total') {
        pmTotalRow = r;
        break;
      }
    }
  }

  const sinFee = findCell_(sheet, ['Sub-Total-1 Presupuesto actividades SIN FEE']);
  const conFee = findCell_(sheet, ['Sub-Total-1 Presupuesto actividades CON FEE']);
  const totalActividad = findCell_(sheet, ['Total De Actividad', 'Total de Actividad']);

  if (sinFee) {
    if (pmTotalRow) sheet.getRange(sinFee.row, 2).setFormula('=E' + pmTotalRow);
    else sheet.getRange(sinFee.row, 2).setValue(0);
  }

  if (conFee) sheet.getRange(conFee.row, 2).setFormula('=E' + activity.grandRow);

  if (totalActividad) {
    const refs = [];
    if (sinFee) refs.push('B' + sinFee.row);
    if (conFee) refs.push('B' + conFee.row);
    if (refs.length) sheet.getRange(totalActividad.row, 2).setFormula('=' + refs.join('+'));
  }
}

function parseDateOrNow_(value) {
  if (value instanceof Date && !isNaN(value.getTime())) return value;
  if (value) {
    const d = new Date(value);
    if (!isNaN(d.getTime())) return d;
  }
  return new Date();
}

// ============================================================================
// 4) GOOGLE SLIDES
// ============================================================================

function generateProposal_(body) {
  const a = body.artifact || {};
  const q = body.quote || {};
  const title = safeName_(a.title || q.project || 'Propuesta Walmart Connect', 'Propuesta Walmart Connect');

  const pres = SlidesApp.create(title);
  const file = DriveApp.getFileById(pres.getId());
  file.moveTo(outputFolder_());

  const slides = Array.isArray(a.slides) && a.slides.length
    ? a.slides
    : [
        { type: 'cover', title: title, subtitle: 'Walmart Connect' },
        { type: 'thankyou', title: 'Thank you!' }
      ];

  // Primero agrega las nuevas diapositivas y al final elimina la inicial automática.
  const initialSlide = pres.getSlides().length ? pres.getSlides()[0] : null;
  for (let i = 0; i < slides.length; i++) {
    addProposalSlide_(pres, slides[i], body, i);
  }
  if (initialSlide && pres.getSlides().length > 1) initialSlide.remove();

  return {
    ok: true,
    artifactType: 'proposal',
    artifactId: pres.getId(),
    artifactUrl: pres.getUrl(),
    title: title,
    provider: 'Google Slides'
  };
}

function addProposalSlide_(pres, spec, body, index) {
  const type = String(spec.type || 'content');
  const slide = pres.appendSlide(SlidesApp.PredefinedLayout.BLANK);
  const pageW = pres.getPageWidth();
  const pageH = pres.getPageHeight();
  const dark = type === 'cover' || type === 'section' || type === 'thankyou';

  slide.getBackground().setSolidFill(dark ? WMC.NAVY : '#FFFFFF');

  const titleText = String(spec.title || (index === 0 ? (body.title || 'Walmart Connect') : ''));
  const titleBox = slide.insertTextBox(
    titleText,
    dark ? 48 : 44,
    dark ? pageH * 0.38 : 28,
    pageW - 96,
    dark ? 90 : 48
  );

  titleBox.getText().getTextStyle()
    .setFontFamily('Arial')
    .setFontSize(dark ? 28 : 22)
    .setBold(true)
    .setForegroundColor(dark ? '#FFFFFF' : WMC.NAVY);

  titleBox.getText().getParagraphStyle().setParagraphAlignment(
    dark ? SlidesApp.ParagraphAlignment.CENTER : SlidesApp.ParagraphAlignment.START
  );

  const sub = String(spec.subtitle || spec.body || '');
  if (sub) {
    const subBox = slide.insertTextBox(
      sub,
      52,
      dark ? pageH * 0.57 : 82,
      pageW - 104,
      70
    );

    subBox.getText().getTextStyle()
      .setFontFamily('Arial')
      .setFontSize(13)
      .setForegroundColor(dark ? '#FFFFFF' : WMC.TEXT);

    if (dark) {
      subBox.getText().getParagraphStyle().setParagraphAlignment(SlidesApp.ParagraphAlignment.CENTER);
    }
  }

  if (!dark) {
    try {
      const blob = visualBlob_({
        imageDriveId: spec.imageDriveId || '',
        imageUrl: spec.imageUrl || '',
        url: spec.imageUrl || ''
      });

      if (blob) {
        const img = slide.insertImage(blob);
        const maxW = pageW - 96;
        const maxH = pageH - 185;
        const scale = Math.min(maxW / img.getWidth(), maxH / img.getHeight());
        const finalW = img.getWidth() * scale;
        const finalH = img.getHeight() * scale;

        img.setWidth(finalW)
          .setHeight(finalH)
          .setLeft((pageW - finalW) / 2)
          .setTop(150);
      }
    } catch (err) {
      console.warn('No se pudo insertar imagen en Slides: ' + err);
    }
  }

  const footer = slide.insertTextBox(
    'PROPRIETARY & CONFIDENTIAL · Walmart Connect / GomezLee Marketing',
    30,
    pageH - 26,
    pageW - 60,
    14
  );

  footer.getText().getTextStyle()
    .setFontFamily('Arial')
    .setFontSize(7)
    .setForegroundColor(dark ? '#D9E2F3' : '#777777');
}

// ============================================================================
// 5) IMAGEN YA GENERADA POR GEMINI EN N8N
// ============================================================================

function generateImageArtifact_(body) {
  const a = body.artifact || {};
  const driveId = String(a.imageDriveId || a.driveId || a.fileId || '').trim();

  if (driveId) {
    const file = DriveApp.getFileById(driveId);
    return {
      ok: true,
      artifactType: 'image',
      artifactId: file.getId(),
      artifactUrl: file.getUrl(),
      title: a.title || file.getName() || 'Visual WMC',
      provider: 'Google Gemini (n8n) + Google Drive'
    };
  }

  const url = String(a.imageUrl || a.url || '').trim();
  if (url) {
    return {
      ok: true,
      artifactType: 'image',
      artifactId: null,
      artifactUrl: url,
      title: a.title || 'Visual WMC',
      provider: 'Google Gemini (n8n)'
    };
  }

  throw new Error('Las imágenes se generan en n8n con el nodo nativo Google Gemini; no se recibió imageDriveId/imageUrl.');
}
