const pptxgen = require("pptxgenjs");
const React = require("react");
const ReactDOMServer = require("react-dom/server");
const sharp = require("sharp");

// Icons from react-icons
const {
  FaCalendarAlt, FaUsers, FaSms, FaMagic, FaShoppingCart, FaUber,
  FaMobileAlt, FaQrcode, FaRoute, FaCogs, FaChartLine, FaLock,
  FaServer, FaDatabase, FaDocker, FaCloudDownloadAlt,
  FaMapMarkerAlt, FaClock, FaBell, FaCheckCircle, FaTags,
  FaNetworkWired, FaToolbox, FaUserShield, FaPaperPlane
} = require("react-icons/fa");
const {
  MdScheduleSend, MdDragIndicator, MdOutlineSmartphone,
  MdOutlineAssignment, MdOutlineQrCodeScanner
} = require("react-icons/md");

function renderIconSvg(IconComponent, color = "#FFFFFF", size = 256) {
  return ReactDOMServer.renderToStaticMarkup(
    React.createElement(IconComponent, { color, size: String(size) })
  );
}

async function iconToBase64Png(IconComponent, color, size = 256) {
  const svg = renderIconSvg(IconComponent, color, size);
  const pngBuffer = await sharp(Buffer.from(svg)).png().toBuffer();
  return "image/png;base64," + pngBuffer.toString("base64");
}

// Color palette — Gigafibre purple theme
const C = {
  darkBg:    "0D0F18",
  cardBg:    "181C2E",
  purple:    "5C59A8",
  purpleDk:  "3F3D7A",
  accent:    "818CF8",
  green:     "22C55E",
  amber:     "F59E0B",
  red:       "EF4444",
  white:     "FFFFFF",
  text:      "E2E4EF",
  muted:     "7B80A0",
  lightBg:   "F5F7FA",
  indigo:    "6366F1",
};

const mkShadow = () => ({ type: "outer", blur: 8, offset: 3, angle: 135, color: "000000", opacity: 0.3 });

async function build() {
  const pres = new pptxgen();
  pres.layout = "LAYOUT_16x9";
  pres.author = "Targo";
  pres.title = "Gigafibre FSM — Plateforme Operations";

  // Pre-render icons
  const icons = {};
  const iconMap = {
    calendar: FaCalendarAlt, users: FaUsers, sms: FaSms, magic: FaMagic,
    cart: FaShoppingCart, uber: FaRoute, mobile: FaMobileAlt,
    qr: FaQrcode, route: FaRoute, cogs: FaCogs, chart: FaChartLine,
    lock: FaLock, server: FaServer, db: FaDatabase, docker: FaDocker,
    cloud: FaCloudDownloadAlt, marker: FaMapMarkerAlt, clock: FaClock,
    bell: FaBell, check: FaCheckCircle, tags: FaTags,
    network: FaNetworkWired, toolbox: FaToolbox, shield: FaUserShield,
    send: FaPaperPlane,
  };
  for (const [k, v] of Object.entries(iconMap)) {
    icons[k] = await iconToBase64Png(v, "#" + C.white, 256);
    icons[k + "_purple"] = await iconToBase64Png(v, "#" + C.accent, 256);
    icons[k + "_dark"] = await iconToBase64Png(v, "#" + C.purpleDk, 256);
  }

  // ═══════════════════════════════════════
  // SLIDE 1 — Title
  // ═══════════════════════════════════════
  let s = pres.addSlide();
  s.background = { color: C.darkBg };
  // Purple accent bar top
  s.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: 10, h: 0.06, fill: { color: C.purple } });
  // Logo area
  s.addShape(pres.shapes.RECTANGLE, { x: 0.8, y: 1.2, w: 0.12, h: 1.8, fill: { color: C.indigo } });
  s.addText("GIGAFIBRE", { x: 1.15, y: 1.2, w: 8, h: 0.7, fontSize: 42, fontFace: "Arial Black", color: C.white, bold: true, margin: 0 });
  s.addText("FSM", { x: 1.15, y: 1.85, w: 8, h: 0.6, fontSize: 36, fontFace: "Arial Black", color: C.accent, margin: 0 });
  s.addText("Plateforme de gestion des operations terrain", { x: 1.15, y: 2.7, w: 7, h: 0.5, fontSize: 18, fontFace: "Calibri", color: C.muted, margin: 0 });
  // Stats row
  const stats = [
    { val: "6 600+", lbl: "Clients" },
    { val: "46", lbl: "Techniciens" },
    { val: "7 500+", lbl: "Equipements" },
    { val: "115K", lbl: "Factures" },
  ];
  stats.forEach((st, i) => {
    const sx = 1.15 + i * 2.1;
    s.addShape(pres.shapes.RECTANGLE, { x: sx, y: 3.7, w: 1.8, h: 1.0, fill: { color: C.cardBg }, shadow: mkShadow() });
    s.addText(st.val, { x: sx, y: 3.72, w: 1.8, h: 0.55, fontSize: 22, fontFace: "Arial Black", color: C.accent, align: "center", valign: "middle", margin: 0 });
    s.addText(st.lbl, { x: sx, y: 4.25, w: 1.8, h: 0.35, fontSize: 11, fontFace: "Calibri", color: C.muted, align: "center", valign: "top", margin: 0 });
  });
  s.addText("Presentation technique — Avril 2026", { x: 0.8, y: 5.1, w: 9, h: 0.3, fontSize: 10, fontFace: "Calibri", color: C.muted, margin: 0 });

  // ═══════════════════════════════════════
  // SLIDE 2 — Architecture Overview
  // ═══════════════════════════════════════
  s = pres.addSlide();
  s.background = { color: C.darkBg };
  s.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: 10, h: 0.06, fill: { color: C.purple } });
  s.addText("Architecture de la plateforme", { x: 0.6, y: 0.25, w: 9, h: 0.6, fontSize: 28, fontFace: "Arial Black", color: C.white, margin: 0 });
  s.addText("Monorepo • Docker • Traefik • ERPNext • Vue 3", { x: 0.6, y: 0.8, w: 9, h: 0.35, fontSize: 12, fontFace: "Calibri", color: C.muted, margin: 0 });

  const archBoxes = [
    { icon: "mobile", title: "Ops App", desc: "Vue 3 / Quasar PWA\n12 pages, 40 composables\n9 modules API", x: 0.4, y: 1.5 },
    { icon: "server", title: "targo-hub", desc: "Node.js 20\n30 modules\nSSE temps reel", x: 2.65, y: 1.5 },
    { icon: "db", title: "ERPNext v16", desc: "PostgreSQL\nDoctypes custom\nAPI REST", x: 4.9, y: 1.5 },
    { icon: "shield", title: "Authentik SSO", desc: "Staff + Client\nForwardAuth\nOAuth/OIDC", x: 7.15, y: 1.5 },
  ];
  for (const b of archBoxes) {
    s.addShape(pres.shapes.RECTANGLE, { x: b.x, y: b.y, w: 2.05, h: 2.3, fill: { color: C.cardBg }, shadow: mkShadow() });
    s.addShape(pres.shapes.RECTANGLE, { x: b.x, y: b.y, w: 2.05, h: 0.06, fill: { color: C.indigo } });
    s.addImage({ data: icons[b.icon], x: b.x + 0.75, y: b.y + 0.25, w: 0.5, h: 0.5 });
    s.addText(b.title, { x: b.x + 0.1, y: b.y + 0.85, w: 1.85, h: 0.35, fontSize: 14, fontFace: "Calibri", color: C.white, bold: true, align: "center", margin: 0 });
    s.addText(b.desc, { x: b.x + 0.1, y: b.y + 1.2, w: 1.85, h: 1.0, fontSize: 10, fontFace: "Calibri", color: C.muted, align: "center", margin: 0 });
  }

  // Integration row
  const integ = [
    { name: "GenieACS", sub: "TR-069 / 7560 CPE" },
    { name: "Twilio", sub: "SMS / Voix" },
    { name: "Mapbox", sub: "Cartes / Routes" },
    { name: "Gemini AI", sub: "OCR / Agent" },
    { name: "Stripe", sub: "Paiements" },
    { name: "Traccar", sub: "GPS temps reel" },
  ];
  s.addText("INTEGRATIONS", { x: 0.6, y: 4.05, w: 9, h: 0.3, fontSize: 10, fontFace: "Calibri", color: C.muted, charSpacing: 3, margin: 0 });
  integ.forEach((it, i) => {
    const ix = 0.4 + i * 1.55;
    s.addShape(pres.shapes.RECTANGLE, { x: ix, y: 4.4, w: 1.4, h: 0.85, fill: { color: "111422" } });
    s.addText(it.name, { x: ix, y: 4.42, w: 1.4, h: 0.4, fontSize: 10, fontFace: "Calibri", color: C.accent, bold: true, align: "center", margin: 0 });
    s.addText(it.sub, { x: ix, y: 4.78, w: 1.4, h: 0.35, fontSize: 8, fontFace: "Calibri", color: C.muted, align: "center", margin: 0 });
  });

  // ═══════════════════════════════════════
  // SLIDE 3 — Dispatch Timeline
  // ═══════════════════════════════════════
  s = pres.addSlide();
  s.background = { color: C.darkBg };
  s.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: 10, h: 0.06, fill: { color: C.purple } });
  s.addText("Dispatch & planification", { x: 0.6, y: 0.25, w: 9, h: 0.6, fontSize: 28, fontFace: "Arial Black", color: C.white, margin: 0 });
  s.addText("Timeline interactive avec drag-and-drop, carte Mapbox, et vues jour/semaine/mois", { x: 0.6, y: 0.8, w: 9, h: 0.35, fontSize: 12, fontFace: "Calibri", color: C.muted, margin: 0 });

  // Feature grid 2x3
  const dispFeats = [
    { icon: "calendar", title: "Timeline Gantt", desc: "Vue jour : rangees par technicien, blocs de jobs colores, drag-and-drop pour assigner ou reordonner" },
    { icon: "route", title: "Routes optimisees", desc: "Calcul d'itineraires Mapbox, temps de deplacement, geo-fixation sur carte" },
    { icon: "tags", title: "Tags / Competences", desc: "Niveaux 1-5 sur techs et jobs. Auto-dispatch : match minimum adequat, preserver les experts" },
    { icon: "clock", title: "Mode planification", desc: "Shifts reguliers en fond bleu, garde en ambre, absences en rouge. Editeur d'horaire inline" },
    { icon: "check", title: "Draft / Publish", desc: "Jobs en brouillon (hachures) puis publication en masse + envoi SMS du resume horaire" },
    { icon: "bell", title: "Recurrence Google", desc: "Selecteur RRULE style Google Calendar avec options contextuelles + editeur personnalise" },
  ];
  dispFeats.forEach((f, i) => {
    const col = i % 3, row = Math.floor(i / 3);
    const fx = 0.4 + col * 3.1, fy = 1.35 + row * 2.05;
    s.addShape(pres.shapes.RECTANGLE, { x: fx, y: fy, w: 2.9, h: 1.8, fill: { color: C.cardBg }, shadow: mkShadow() });
    // Icon circle
    s.addShape(pres.shapes.OVAL, { x: fx + 0.2, y: fy + 0.2, w: 0.55, h: 0.55, fill: { color: C.purpleDk } });
    s.addImage({ data: icons[f.icon], x: fx + 0.3, y: fy + 0.3, w: 0.35, h: 0.35 });
    s.addText(f.title, { x: fx + 0.9, y: fy + 0.2, w: 1.85, h: 0.4, fontSize: 13, fontFace: "Calibri", color: C.white, bold: true, margin: 0, valign: "middle" });
    s.addText(f.desc, { x: fx + 0.2, y: fy + 0.85, w: 2.5, h: 0.85, fontSize: 9.5, fontFace: "Calibri", color: C.muted, margin: 0 });
  });

  // ═══════════════════════════════════════
  // SLIDE 4 — Gestion des horaires
  // ═══════════════════════════════════════
  s = pres.addSlide();
  s.background = { color: C.darkBg };
  s.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: 10, h: 0.06, fill: { color: C.purple } });
  s.addText("Gestion des horaires techniciens", { x: 0.6, y: 0.25, w: 9, h: 0.6, fontSize: 28, fontFace: "Arial Black", color: C.white, margin: 0 });

  // Left: schedule editor mockup
  s.addShape(pres.shapes.RECTANGLE, { x: 0.5, y: 1.1, w: 4.3, h: 4.1, fill: { color: C.cardBg }, shadow: mkShadow() });
  s.addText("Editeur d'horaire", { x: 0.7, y: 1.2, w: 3.9, h: 0.4, fontSize: 14, fontFace: "Calibri", color: C.white, bold: true, margin: 0 });

  const days = ["Lun", "Mar", "Mer", "Jeu", "Ven", "Sam", "Dim"];
  const schedActive = [true, true, true, true, true, false, false];
  days.forEach((d, i) => {
    const dy = 1.75 + i * 0.48;
    s.addShape(pres.shapes.RECTANGLE, { x: 0.7, y: dy, w: 3.9, h: 0.38, fill: { color: schedActive[i] ? "1E2338" : "141726" } });
    s.addText(d, { x: 0.8, y: dy, w: 0.6, h: 0.38, fontSize: 10, fontFace: "Calibri", color: schedActive[i] ? C.green : C.muted, bold: true, valign: "middle", margin: 0 });
    if (schedActive[i]) {
      s.addText("08:00 → 16:00", { x: 1.5, y: dy, w: 2.0, h: 0.38, fontSize: 10, fontFace: "Calibri", color: C.text, valign: "middle", margin: 0 });
    } else {
      s.addText("Repos", { x: 1.5, y: dy, w: 2.0, h: 0.38, fontSize: 10, fontFace: "Calibri", color: C.muted, italic: true, valign: "middle", margin: 0 });
    }
  });

  // Garde shift
  s.addText("Shifts de garde", { x: 0.7, y: 5.2 - 0.85, w: 3.9, h: 0.3, fontSize: 11, fontFace: "Calibri", color: C.amber, bold: true, margin: 0 });
  s.addShape(pres.shapes.RECTANGLE, { x: 0.7, y: 5.2 - 0.48, w: 3.9, h: 0.38, fill: { color: "2A2510" } });
  s.addText("Garde   08:00 → 16:00   1 fin de semaine sur 4", { x: 0.8, y: 5.2 - 0.48, w: 3.7, h: 0.38, fontSize: 9, fontFace: "Calibri", color: C.amber, valign: "middle", margin: 0 });

  // Right: features
  s.addShape(pres.shapes.RECTANGLE, { x: 5.2, y: 1.1, w: 4.3, h: 4.1, fill: { color: C.cardBg }, shadow: mkShadow() });
  const schedFeats = [
    { title: "Horaire hebdomadaire", desc: "Configuration par jour, presets (temps plein, soirs, nuits)" },
    { title: "Shifts de garde (RRULE)", desc: "Recurrence flexible : 1 weekend sur N, soirs de semaine, personnalise" },
    { title: "RecurrenceSelector", desc: "Composant Google Calendar : options contextuelles + editeur custom RRULE" },
    { title: "Visualisation timeline", desc: "Blocs bleus (regulier), ambres (garde) en fond du timeline" },
    { title: "Presets partages", desc: "Groupes de ressources sauvegardes dans ERPNext, partages entre superviseurs" },
  ];
  schedFeats.forEach((f, i) => {
    const fy = 1.3 + i * 0.78;
    s.addShape(pres.shapes.OVAL, { x: 5.4, y: fy + 0.05, w: 0.28, h: 0.28, fill: { color: C.purpleDk } });
    s.addText(String(i + 1), { x: 5.4, y: fy + 0.05, w: 0.28, h: 0.28, fontSize: 9, fontFace: "Calibri", color: C.white, bold: true, align: "center", valign: "middle", margin: 0 });
    s.addText(f.title, { x: 5.85, y: fy, w: 3.4, h: 0.3, fontSize: 12, fontFace: "Calibri", color: C.white, bold: true, margin: 0 });
    s.addText(f.desc, { x: 5.85, y: fy + 0.3, w: 3.4, h: 0.35, fontSize: 9, fontFace: "Calibri", color: C.muted, margin: 0 });
  });

  // ═══════════════════════════════════════
  // SLIDE 5 — Attribution de jobs & SMS
  // ═══════════════════════════════════════
  s = pres.addSlide();
  s.background = { color: C.darkBg };
  s.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: 10, h: 0.06, fill: { color: C.purple } });
  s.addText("Attribution de jobs & communication", { x: 0.6, y: 0.25, w: 9, h: 0.6, fontSize: 28, fontFace: "Arial Black", color: C.white, margin: 0 });

  // Flow diagram
  const flowSteps = [
    { icon: "calendar", title: "1. Planifier", desc: "Drag-and-drop sur\nle timeline", color: C.indigo },
    { icon: "send", title: "2. Publier", desc: "Selection en masse\n+ confirmation", color: C.purple },
    { icon: "sms", title: "3. SMS", desc: "Resume horaire\nenvoye via Twilio", color: C.green },
    { icon: "mobile", title: "4. Lien tech", desc: "Vue mobile /j\navec bottom sheet", color: C.amber },
  ];
  flowSteps.forEach((f, i) => {
    const fx = 0.4 + i * 2.4;
    s.addShape(pres.shapes.RECTANGLE, { x: fx, y: 1.2, w: 2.15, h: 1.8, fill: { color: C.cardBg }, shadow: mkShadow() });
    s.addShape(pres.shapes.RECTANGLE, { x: fx, y: 1.2, w: 2.15, h: 0.06, fill: { color: f.color } });
    s.addShape(pres.shapes.OVAL, { x: fx + 0.75, y: 1.4, w: 0.6, h: 0.6, fill: { color: f.color, transparency: 70 } });
    s.addImage({ data: icons[f.icon], x: fx + 0.85, y: 1.5, w: 0.4, h: 0.4 });
    s.addText(f.title, { x: fx + 0.1, y: 2.15, w: 1.95, h: 0.35, fontSize: 13, fontFace: "Calibri", color: C.white, bold: true, align: "center", margin: 0 });
    s.addText(f.desc, { x: fx + 0.1, y: 2.5, w: 1.95, h: 0.45, fontSize: 10, fontFace: "Calibri", color: C.muted, align: "center", margin: 0 });
  });

  // SMS example mockup
  s.addShape(pres.shapes.RECTANGLE, { x: 0.5, y: 3.3, w: 4.3, h: 2.0, fill: { color: C.cardBg }, shadow: mkShadow() });
  s.addImage({ data: icons.sms_purple, x: 0.7, y: 3.45, w: 0.35, h: 0.35 });
  s.addText("Envoi SMS (Twilio)", { x: 1.15, y: 3.45, w: 3.3, h: 0.35, fontSize: 13, fontFace: "Calibri", color: C.white, bold: true, margin: 0, valign: "middle" });
  s.addShape(pres.shapes.RECTANGLE, { x: 0.7, y: 3.95, w: 3.9, h: 1.15, fill: { color: "111422" } });
  s.addText([
    { text: "Bonjour Louis-Paul,\n", options: { fontSize: 9, color: C.text, breakLine: true } },
    { text: "Votre horaire pour le 9 avril :\n", options: { fontSize: 9, color: C.text, breakLine: true } },
    { text: "08h00 - SUP-003 Panne IP Phone\n", options: { fontSize: 9, color: C.accent, breakLine: true } },
    { text: "10h30 - INS-047 Installation Fibre\n", options: { fontSize: 9, color: C.accent, breakLine: true } },
    { text: "erp.gigafibre.ca/ops/#/j", options: { fontSize: 8, color: C.green } },
  ], { x: 0.85, y: 4.0, w: 3.6, h: 1.05, margin: 0 });

  // Magic links
  s.addShape(pres.shapes.RECTANGLE, { x: 5.2, y: 3.3, w: 4.3, h: 2.0, fill: { color: C.cardBg }, shadow: mkShadow() });
  s.addImage({ data: icons.magic_purple, x: 5.4, y: 3.45, w: 0.35, h: 0.35 });
  s.addText("Magic Links & OTP", { x: 5.85, y: 3.45, w: 3.3, h: 0.35, fontSize: 13, fontFace: "Calibri", color: C.white, bold: true, margin: 0, valign: "middle" });
  const mlFeats = [
    "Authentification sans mot de passe",
    "Lien unique par SMS ou email",
    "OTP 6 chiffres via Twilio",
    "Migration transparente legacy MD5",
    "Session SSO via Authentik",
  ];
  mlFeats.forEach((f, i) => {
    s.addImage({ data: icons.check_purple, x: 5.4, y: 3.95 + i * 0.22, w: 0.15, h: 0.15 });
    s.addText(f, { x: 5.7, y: 3.95 + i * 0.22, w: 3.5, h: 0.22, fontSize: 10, fontFace: "Calibri", color: C.text, margin: 0, valign: "middle" });
  });

  // ═══════════════════════════════════════
  // SLIDE 6 — Uber-style offer pool
  // ═══════════════════════════════════════
  s = pres.addSlide();
  s.background = { color: C.darkBg };
  s.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: 10, h: 0.06, fill: { color: C.purple } });
  s.addText("Pool d'offres — Style Uber", { x: 0.6, y: 0.25, w: 9, h: 0.6, fontSize: 28, fontFace: "Arial Black", color: C.white, margin: 0 });
  s.addText("Proposer des taches aux ressources internes et externes avec tarification dynamique", { x: 0.6, y: 0.8, w: 9, h: 0.35, fontSize: 12, fontFace: "Calibri", color: C.muted, margin: 0 });

  // 3 mode cards
  const modes = [
    { title: "Broadcast", desc: "Envoyer a toutes les\nressources disponibles", icon: "bell", color: C.indigo },
    { title: "Targeted", desc: "Selectionner des\ntechniciens specifiques", icon: "users", color: C.green },
    { title: "Pool", desc: "Match par competences\net disponibilite", icon: "tags", color: C.amber },
  ];
  modes.forEach((m, i) => {
    const mx = 0.4 + i * 3.15;
    s.addShape(pres.shapes.RECTANGLE, { x: mx, y: 1.3, w: 2.95, h: 1.5, fill: { color: C.cardBg }, shadow: mkShadow() });
    s.addShape(pres.shapes.OVAL, { x: mx + 0.2, y: 1.5, w: 0.5, h: 0.5, fill: { color: m.color, transparency: 70 } });
    s.addImage({ data: icons[m.icon], x: mx + 0.28, y: 1.58, w: 0.35, h: 0.35 });
    s.addText(m.title, { x: mx + 0.85, y: 1.45, w: 1.9, h: 0.35, fontSize: 14, fontFace: "Calibri", color: C.white, bold: true, margin: 0, valign: "middle" });
    s.addText(m.desc, { x: mx + 0.85, y: 1.8, w: 1.9, h: 0.5, fontSize: 10, fontFace: "Calibri", color: C.muted, margin: 0 });
  });

  // Pricing section
  s.addShape(pres.shapes.RECTANGLE, { x: 0.4, y: 3.1, w: 4.5, h: 2.2, fill: { color: C.cardBg }, shadow: mkShadow() });
  s.addText("Tarification", { x: 0.6, y: 3.2, w: 4.1, h: 0.4, fontSize: 14, fontFace: "Calibri", color: C.white, bold: true, margin: 0 });
  const pricing = [
    { label: "Deplacement (base)", val: "150 $", color: C.accent },
    { label: "Taux horaire", val: "125 $/h", color: C.accent },
    { label: "Majoration urgence", val: "+50%", color: C.red },
    { label: "Weekend / ferie", val: "+75%", color: C.amber },
  ];
  pricing.forEach((p, i) => {
    const py = 3.7 + i * 0.4;
    s.addText(p.label, { x: 0.7, y: py, w: 2.5, h: 0.35, fontSize: 11, fontFace: "Calibri", color: C.text, margin: 0, valign: "middle" });
    s.addText(p.val, { x: 3.2, y: py, w: 1.5, h: 0.35, fontSize: 13, fontFace: "Arial Black", color: p.color, margin: 0, valign: "middle", align: "right" });
  });

  // Flow
  s.addShape(pres.shapes.RECTANGLE, { x: 5.2, y: 3.1, w: 4.3, h: 2.2, fill: { color: C.cardBg }, shadow: mkShadow() });
  s.addText("Flux d'offre", { x: 5.4, y: 3.2, w: 3.9, h: 0.4, fontSize: 14, fontFace: "Calibri", color: C.white, bold: true, margin: 0 });
  const offerFlow = [
    { step: "1", text: "Superviseur cree l'offre (cout estime)" },
    { step: "2", text: "Notification SMS aux techniciens cibles" },
    { step: "3", text: "Technicien accepte ou decline" },
    { step: "4", text: "Job auto-assigne au premier accepte" },
    { step: "5", text: "Alerte surcharge si capacite > 100%" },
  ];
  offerFlow.forEach((f, i) => {
    const fy = 3.7 + i * 0.32;
    s.addShape(pres.shapes.OVAL, { x: 5.4, y: fy + 0.03, w: 0.24, h: 0.24, fill: { color: C.indigo } });
    s.addText(f.step, { x: 5.4, y: fy + 0.03, w: 0.24, h: 0.24, fontSize: 8, fontFace: "Calibri", color: C.white, bold: true, align: "center", valign: "middle", margin: 0 });
    s.addText(f.text, { x: 5.8, y: fy, w: 3.5, h: 0.28, fontSize: 10, fontFace: "Calibri", color: C.text, margin: 0, valign: "middle" });
  });

  // ═══════════════════════════════════════
  // SLIDE 7 — Portail client & Catalogue
  // ═══════════════════════════════════════
  s = pres.addSlide();
  s.background = { color: C.darkBg };
  s.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: 10, h: 0.06, fill: { color: C.purple } });
  s.addText("Portail client & catalogue", { x: 0.6, y: 0.25, w: 9, h: 0.6, fontSize: 28, fontFace: "Arial Black", color: C.white, margin: 0 });
  s.addText("Self-service client avec Stripe, catalogue produits, et gestion de compte", { x: 0.6, y: 0.8, w: 9, h: 0.35, fontSize: 12, fontFace: "Calibri", color: C.muted, margin: 0 });

  // Client portal features — left
  s.addShape(pres.shapes.RECTANGLE, { x: 0.4, y: 1.3, w: 4.5, h: 3.7, fill: { color: C.cardBg }, shadow: mkShadow() });
  s.addImage({ data: icons.cart_purple, x: 0.6, y: 1.45, w: 0.4, h: 0.4 });
  s.addText("Portail client (client.gigafibre.ca)", { x: 1.1, y: 1.45, w: 3.5, h: 0.4, fontSize: 14, fontFace: "Calibri", color: C.white, bold: true, margin: 0, valign: "middle" });

  const portalFeats = [
    "Consultation factures et historique",
    "Paiement en ligne via Stripe Checkout",
    "Gestion des abonnements actifs",
    "Tickets de support (creation + suivi)",
    "Catalogue produits avec panier",
    "Checkout + soumission commande",
    "Authentification SSO (id.gigafibre.ca)",
    "OTP SMS pour recuperation de compte",
  ];
  portalFeats.forEach((f, i) => {
    s.addImage({ data: icons.check_purple, x: 0.7, y: 2.05 + i * 0.35, w: 0.18, h: 0.18 });
    s.addText(f, { x: 1.0, y: 2.05 + i * 0.35, w: 3.7, h: 0.3, fontSize: 11, fontFace: "Calibri", color: C.text, margin: 0, valign: "middle" });
  });

  // Catalogue — right
  s.addShape(pres.shapes.RECTANGLE, { x: 5.2, y: 1.3, w: 4.3, h: 3.7, fill: { color: C.cardBg }, shadow: mkShadow() });
  s.addImage({ data: icons.toolbox_purple, x: 5.4, y: 1.45, w: 0.4, h: 0.4 });
  s.addText("Catalogue & wizard", { x: 5.9, y: 1.45, w: 3.3, h: 0.4, fontSize: 14, fontFace: "Calibri", color: C.white, bold: true, margin: 0, valign: "middle" });

  const catalogItems = [
    { name: "Internet Fibre", price: "a partir de 55$/mois" },
    { name: "Television IPTV", price: "20$/mois" },
    { name: "Telephonie VoIP", price: "15$/mois" },
    { name: "Support Prioritaire", price: "10$/mois" },
  ];
  catalogItems.forEach((it, i) => {
    const cy = 2.1 + i * 0.7;
    s.addShape(pres.shapes.RECTANGLE, { x: 5.4, y: cy, w: 3.9, h: 0.55, fill: { color: "1E2338" } });
    s.addText(it.name, { x: 5.6, y: cy, w: 2.4, h: 0.55, fontSize: 12, fontFace: "Calibri", color: C.white, bold: true, margin: 0, valign: "middle" });
    s.addText(it.price, { x: 7.8, y: cy, w: 1.4, h: 0.55, fontSize: 11, fontFace: "Calibri", color: C.green, margin: 0, valign: "middle", align: "right" });
  });

  // ═══════════════════════════════════════
  // SLIDE 8 — Vue mobile technicien
  // ═══════════════════════════════════════
  s = pres.addSlide();
  s.background = { color: C.darkBg };
  s.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: 10, h: 0.06, fill: { color: C.purple } });
  s.addText("Vue mobile technicien", { x: 0.6, y: 0.25, w: 9, h: 0.6, fontSize: 28, fontFace: "Arial Black", color: C.white, margin: 0 });
  s.addText("Integree dans l'ops app a /j — envoyee par SMS apres publication de l'horaire", { x: 0.6, y: 0.8, w: 9, h: 0.35, fontSize: 12, fontFace: "Calibri", color: C.muted, margin: 0 });

  // Phone mockup - left
  s.addShape(pres.shapes.RECTANGLE, { x: 0.8, y: 1.3, w: 3.0, h: 4.0, fill: { color: "1A1D30" }, shadow: mkShadow() });
  s.addShape(pres.shapes.RECTANGLE, { x: 0.8, y: 1.3, w: 3.0, h: 0.06, fill: { color: C.purple } });
  // Phone header
  s.addShape(pres.shapes.RECTANGLE, { x: 0.8, y: 1.36, w: 3.0, h: 1.0, fill: { color: C.purpleDk } });
  s.addText("jeudi 9 avril", { x: 0.95, y: 1.4, w: 2.5, h: 0.25, fontSize: 8, fontFace: "Calibri", color: C.muted, margin: 0 });
  s.addText("Louis-Paul Bourdon", { x: 0.95, y: 1.6, w: 2.5, h: 0.3, fontSize: 12, fontFace: "Calibri", color: C.white, bold: true, margin: 0 });
  // Stats mini
  const miniStats = [{ v: "3", l: "Total" }, { v: "3", l: "A faire" }, { v: "0", l: "Faits" }];
  miniStats.forEach((ms, i) => {
    s.addShape(pres.shapes.RECTANGLE, { x: 0.95 + i * 0.85, y: 2.0, w: 0.75, h: 0.42, fill: { color: "4A4880" } });
    s.addText(ms.v, { x: 0.95 + i * 0.85, y: 2.0, w: 0.75, h: 0.25, fontSize: 11, fontFace: "Arial Black", color: C.white, align: "center", margin: 0 });
    s.addText(ms.l, { x: 0.95 + i * 0.85, y: 2.22, w: 0.75, h: 0.18, fontSize: 6, fontFace: "Calibri", color: C.muted, align: "center", margin: 0 });
  });
  // Job cards mini
  const miniJobs = [
    { id: "SUP-003", title: "Panne IP Phone", loc: "Vieux-Port", time: "08h09", urgent: true },
    { id: "INS-047", title: "Installation Fibre", loc: "Plateau", time: "10h30", urgent: false },
  ];
  miniJobs.forEach((mj, i) => {
    const my = 2.6 + i * 0.85;
    s.addShape(pres.shapes.RECTANGLE, { x: 0.95, y: my, w: 2.5, h: 0.7, fill: { color: C.cardBg } });
    s.addShape(pres.shapes.RECTANGLE, { x: 0.95, y: my, w: 0.06, h: 0.7, fill: { color: mj.urgent ? C.red : C.purple } });
    s.addText(mj.id, { x: 1.1, y: my + 0.03, w: 1.0, h: 0.2, fontSize: 7, fontFace: "Calibri", color: C.accent, bold: true, margin: 0 });
    s.addText(mj.time, { x: 2.85, y: my + 0.03, w: 0.55, h: 0.2, fontSize: 7, fontFace: "Calibri", color: C.text, margin: 0, align: "right" });
    s.addText(mj.title, { x: 1.1, y: my + 0.22, w: 2.2, h: 0.22, fontSize: 8, fontFace: "Calibri", color: C.white, bold: true, margin: 0 });
    s.addText(mj.loc, { x: 1.1, y: my + 0.45, w: 2.2, h: 0.2, fontSize: 7, fontFace: "Calibri", color: C.muted, margin: 0 });
  });

  // Features — right
  const techFeats = [
    { icon: "check", title: "Actions rapides", desc: "En route / Terminer depuis la liste ou le bottom sheet. GPS navigation integree." },
    { icon: "qr", title: "Scanner equipement", desc: "Recherche par SN/MAC, creation inline, liaison automatique au job en cours." },
    { icon: "cogs", title: "Edition inline", desc: "Modifier sujet, heure, duree, notes directement depuis le telephone." },
    { icon: "chart", title: "Diagnostic reseau", desc: "Speed test, verification d'hotes, latence — directement depuis le terrain." },
  ];
  techFeats.forEach((f, i) => {
    const fy = 1.3 + i * 1.0;
    s.addShape(pres.shapes.RECTANGLE, { x: 4.5, y: fy, w: 5.1, h: 0.85, fill: { color: C.cardBg }, shadow: mkShadow() });
    s.addShape(pres.shapes.OVAL, { x: 4.7, y: fy + 0.15, w: 0.5, h: 0.5, fill: { color: C.purpleDk } });
    s.addImage({ data: icons[f.icon], x: 4.8, y: fy + 0.25, w: 0.3, h: 0.3 });
    s.addText(f.title, { x: 5.35, y: fy + 0.05, w: 4.0, h: 0.3, fontSize: 13, fontFace: "Calibri", color: C.white, bold: true, margin: 0 });
    s.addText(f.desc, { x: 5.35, y: fy + 0.35, w: 4.0, h: 0.45, fontSize: 10, fontFace: "Calibri", color: C.muted, margin: 0 });
  });

  // ═══════════════════════════════════════
  // SLIDE 9 — Stack technique
  // ═══════════════════════════════════════
  s = pres.addSlide();
  s.background = { color: C.darkBg };
  s.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: 10, h: 0.06, fill: { color: C.purple } });
  s.addText("Stack technique", { x: 0.6, y: 0.25, w: 9, h: 0.6, fontSize: 28, fontFace: "Arial Black", color: C.white, margin: 0 });

  const techStack = [
    { cat: "Frontend", items: "Vue 3 (Composition API)\nQuasar v2.19 (PWA)\nPinia stores\nMapbox GL JS\nSCSS + CSS variables" },
    { cat: "Backend", items: "Node.js 20 (targo-hub)\nERPNext v16 (Python)\nn8n (automations)\nPostgreSQL 14\nRedis (cache/queue)" },
    { cat: "Infra", items: "Docker Compose\nTraefik v2.11 (TLS)\nAuthentik SSO\nLet's Encrypt\nProxmox VM" },
    { cat: "Integrations", items: "Twilio (SMS/Voix)\nGenieACS (TR-069)\nStripe (Paiements)\nGemini AI (OCR)\nCloudflare (DNS)" },
  ];
  techStack.forEach((ts, i) => {
    const tx = 0.4 + i * 2.35;
    s.addShape(pres.shapes.RECTANGLE, { x: tx, y: 1.1, w: 2.15, h: 3.5, fill: { color: C.cardBg }, shadow: mkShadow() });
    s.addShape(pres.shapes.RECTANGLE, { x: tx, y: 1.1, w: 2.15, h: 0.06, fill: { color: i === 0 ? C.indigo : i === 1 ? C.green : i === 2 ? C.amber : C.red } });
    s.addText(ts.cat, { x: tx + 0.15, y: 1.3, w: 1.85, h: 0.4, fontSize: 15, fontFace: "Calibri", color: C.white, bold: true, margin: 0 });
    s.addText(ts.items, { x: tx + 0.15, y: 1.8, w: 1.85, h: 2.6, fontSize: 11, fontFace: "Calibri", color: C.muted, margin: 0 });
  });

  // Deployment
  s.addShape(pres.shapes.RECTANGLE, { x: 0.4, y: 4.85, w: 9.2, h: 0.55, fill: { color: C.cardBg } });
  s.addText([
    { text: "Deploy:  ", options: { bold: true, color: C.white, fontSize: 11 } },
    { text: "npx quasar build", options: { color: C.green, fontSize: 11, fontFace: "Consolas" } },
    { text: "  →  ", options: { color: C.muted, fontSize: 11 } },
    { text: "scp dist/spa/* root@96.125.196.67:/opt/ops-app/", options: { color: C.accent, fontSize: 10, fontFace: "Consolas" } },
    { text: "  (no restart needed)", options: { color: C.muted, fontSize: 10 } },
  ], { x: 0.6, y: 4.85, w: 8.8, h: 0.55, margin: 0, valign: "middle" });

  // ═══════════════════════════════════════
  // SLIDE 10 — Closing
  // ═══════════════════════════════════════
  s = pres.addSlide();
  s.background = { color: C.darkBg };
  s.addShape(pres.shapes.RECTANGLE, { x: 0, y: 0, w: 10, h: 0.06, fill: { color: C.purple } });
  s.addShape(pres.shapes.RECTANGLE, { x: 0.8, y: 1.8, w: 0.12, h: 1.8, fill: { color: C.indigo } });
  s.addText("GIGAFIBRE FSM", { x: 1.15, y: 1.8, w: 8, h: 0.7, fontSize: 36, fontFace: "Arial Black", color: C.white, margin: 0 });
  s.addText("Questions ?", { x: 1.15, y: 2.5, w: 8, h: 0.5, fontSize: 22, fontFace: "Calibri", color: C.accent, margin: 0 });

  const urls = [
    { lbl: "Ops App", url: "erp.gigafibre.ca/ops/" },
    { lbl: "ERPNext", url: "erp.gigafibre.ca" },
    { lbl: "Vue Tech", url: "erp.gigafibre.ca/ops/#/j" },
    { lbl: "Portail", url: "client.gigafibre.ca" },
    { lbl: "Git", url: "git.targo.ca/louis/gigafibre-fsm" },
  ];
  urls.forEach((u, i) => {
    s.addText(u.lbl, { x: 1.15, y: 3.4 + i * 0.35, w: 1.5, h: 0.3, fontSize: 12, fontFace: "Calibri", color: C.muted, margin: 0, valign: "middle" });
    s.addText(u.url, { x: 2.6, y: 3.4 + i * 0.35, w: 5, h: 0.3, fontSize: 12, fontFace: "Consolas", color: C.accent, margin: 0, valign: "middle" });
  });

  s.addText("Targo — Avril 2026", { x: 0.8, y: 5.1, w: 9, h: 0.3, fontSize: 10, fontFace: "Calibri", color: C.muted, margin: 0 });

  // Write file
  const path = require("path");
  await pres.writeFile({ fileName: path.join(__dirname, "Gigafibre-FSM-Features.pptx") });
  console.log("Presentation created!");
}

build().catch(e => { console.error(e); process.exit(1); });
