/* ADMIN · Importar Dados — upload de XLSX do Primavera (Vendas + Stocks)
   Gera window._VP_DRILL em memória + guarda em localStorage com timestamp.
   SheetJS carregado dinamicamente (só quando o utilizador entra nesta página). */

// ── SheetJS loader (lazy) ──────────────────────────────────────────────────
function loadSheetJS() {
  return new Promise((resolve, reject) => {
    if (window.XLSX) return resolve(window.XLSX);
    const s = document.createElement('script');
    s.src = 'https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js';
    s.onload = () => resolve(window.XLSX);
    s.onerror = () => reject(new Error('Falha ao carregar SheetJS'));
    document.head.appendChild(s);
  });
}

// ── Lookups para paises e grupos ───────────────────────────────────────────
const _ISO_NOME = {
  PT:'Portugal',ES:'Espanha',FR:'França',IT:'Itália',DE:'Deutschland',
  GB:'United Kingdom',US:'Estados Unidos',NL:'Holanda',CZ:'Rep. Checa',
  AO:'Angola',AU:'Australia',CA:'Canadá',BE:'Bélgica',PL:'Polónia',
  AT:'Austria',CH:'Suíça',SE:'Suécia',DK:'Dinamarca',NO:'Noruega',
  FI:'Finlândia',IE:'Irlanda',BR:'Brasil',MX:'México',AR:'Argentina',
  ZA:'África do Sul',CN:'China',JP:'Japão',KR:'Coreia do Sul',
  IN:'Índia',IL:'Israel',MA:'Marrocos',NG:'Nigéria',TR:'Turquia',
  RU:'Rússia',UA:'Ucrânia',RO:'Roménia',HU:'Hungria',SK:'Eslováquia',
  HR:'Croácia',RS:'Sérvia',BG:'Bulgária',GR:'Grécia',LU:'Luxemburgo',
  CY:'Chipre',MT:'Malta',EE:'Estónia',LV:'Letónia',LT:'Lituânia',
  SI:'Eslovénia',SA:'Arábia Saudita',AE:'Emirados Árabes',QA:'Qatar',
  MO:'Macau',HK:'Hong Kong',SG:'Singapura',MY:'Malásia',TH:'Tailândia',
  NZ:'Nova Zelândia',CL:'Chile',CO:'Colômbia',PE:'Peru',
};
// Inferência de grupo a partir do nome do vendedor (convenção Decal)
const _GRUPO_INFER = (nome) => {
  if (!nome) return 'Outros';
  if (/sensek/i.test(nome))           return 'Sensek';
  if (/mimaki/i.test(nome))           return 'Mimaki';
  if (/\bsede\b/i.test(nome))         return 'Sede';
  if (/decal\s+es\s+dim/i.test(nome)) return 'Grupo';
  if (/decal\s+pa\b/i.test(nome))     return 'Partners';
  if (/decal\s+pt\b/i.test(nome))     return 'Nacional';
  if (/decal\b/i.test(nome))          return 'Export';
  return 'Outros';
};

// ── Parser de vendas → _VP_DRILL + _VP_DATA ────────────────────────────────
function processVendas(rows) {
  const MONTH_LBL = {1:'Jan',2:'Fev',3:'Mar',4:'Abr',5:'Mai',6:'Jun',7:'Jul',8:'Ago',9:'Set',10:'Out',11:'Nov',12:'Dez'};
  const r2 = (v) => Math.round(v * 100) / 100;
  const r1 = (v) => Math.round(v * 10) / 10;
  const pct = (m, v) => v !== 0 ? r1((m / v) * 100) : 0;
  const vvar = (v26, v25) => v25 !== 0 ? r1(((v26 - v25) / Math.abs(v25)) * 100) : (v26 !== 0 ? 100 : 0);

  // ─── pré-leitura: detecta o máximo mês de 2026 (para YTD 2025 comparável) ─
  const toJsDate = (date) =>
    (date instanceof Date) ? date
    : (typeof date === 'number') ? new Date(Math.round((date - 25569) * 86400 * 1000))
    : null;
  let maxM26 = 0;
  for (const row of rows) {
    const jd = toJsDate(row['Data']);
    if (jd && jd.getFullYear() === 2026) {
      const m = jd.getMonth() + 1;
      if (m > maxM26) maxM26 = m;
    }
  }
  const ytdCutoff = maxM26 || 12; // meses de 2025 a incluir nas comparações YTD

  // Detecta colunas opcionais
  const firstRow = rows[0] ? Object.keys(rows[0]) : [];
  const DOC_COL      = firstRow.find(k => /documento|nº doc|n\.º doc|doc\b/i.test(k)) || null;
  const PAIS_COL     = firstRow.find(k => /^pa[ií]s$/i.test(k)) || null;
  const VEND_COL     = firstRow.find(k => /^vendedor$/i.test(k)) || null;
  const VEND_NOME_COL = firstRow.find(k => /^nome\s+vendedor$/i.test(k)) || null;
  const docs26 = new Set(), docs25 = new Set();

  // ─── accumulators ─────────────────────────────────────────────────────────
  const raw = {};        // fam → ent → { e, n, arts:{} }  (para drill)
  const famRaw = {};     // fam → { f, q26,q25, v26,v25, c26,c25, m26,m25 }
  const artRaw = {};     // art → { a, d, f, q26,q25, v26,v25, c26,c25, m26,m25 }
  const cliRaw = {};     // ent → { e, n, q26,q25, v26,v25, m26,m25 }
  const paisRaw = {};    // iso → { v26,v25,m26,m25, ents26:Set, ents25:Set }
  const vendRaw = {};    // code → { nome, v26,v25,m26,m25, ents26:Set, ents25:Set, docs26:Set, docs25:Set }
  const lastDate = {};   // ent → última data 2025 (string YYYY-MM-DD)
  const monthly = {};
  for (let m = 1; m <= 12; m++) monthly[m] = { y25: 0, y26: 0 };
  let kv26=0, kv25=0, km26=0, km25=0, n=0;
  const ents26 = new Set(), ents25 = new Set(), arts26 = new Set(), arts25 = new Set();

  // ─── passagem 1: acumula ──────────────────────────────────────────────────
  for (const row of rows) {
    const fam  = (row['Família (Descrição)'] || '').trim();
    if (!fam) continue;
    const ent  = String(row['Entidade'] || '').trim();
    const nome = (row['Nome'] || '').trim();
    const art  = (row['Artigo'] || '').trim();
    const desc = (row['Artigo (Descrição)'] || '').trim();
    const qty  = Number(row['Quantidade']) || 0;
    const vliq = Number(row['V. Líquido']) || 0;
    const custo = Number(row['Custo']) || 0;
    const marg = Number(row['Margem']) || 0;
    const jsDate = toJsDate(row['Data']);
    const year  = jsDate ? jsDate.getFullYear() : 0;
    const month = jsDate ? (jsDate.getMonth() + 1) : 0;
    if (year !== 2025 && year !== 2026) continue;
    // 2025: só incluir meses YTD comparáveis (≤ maxM26) nas comparações
    const inYTD = year === 2026 || (year === 2025 && month <= ytdCutoff);

    // gráfico mensal: 2025 ano inteiro, 2026 YTD
    if (month >= 1 && month <= 12) {
      if (year === 2026) monthly[month].y26 += vliq;
      else               monthly[month].y25 += vliq;
    }

    // comparações (KPIs, drill, fam, art, cli): só meses YTD comparáveis
    if (!inYTD) { n++; continue; }

    // drill raw
    if (!raw[fam]) raw[fam] = {};
    if (!raw[fam][ent]) raw[fam][ent] = { e: ent, n: nome, arts: {} };
    const cliD = raw[fam][ent];
    if (!cliD.arts[art]) cliD.arts[art] = { a: art, d: desc, q26:0, q25:0, v26:0, v25:0, m26:0, m25:0, cr: 0 };
    const aD = cliD.arts[art];

    // fam / art / cli raw init
    if (!famRaw[fam]) famRaw[fam] = { f: fam, q26:0, q25:0, v26:0, v25:0, c26:0, c25:0, m26:0, m25:0 };
    if (!artRaw[art]) artRaw[art] = { a: art, d: desc, f: fam, q26:0, q25:0, v26:0, v25:0, c26:0, c25:0, m26:0, m25:0 };
    if (!cliRaw[ent]) cliRaw[ent] = { e: ent, n: nome, q26:0, q25:0, v26:0, v25:0, m26:0, m25:0 };

    const doc = DOC_COL ? String(row[DOC_COL] || '').trim() : null;

    if (year === 2026) {
      aD.q26 += qty; aD.v26 += vliq; aD.m26 += marg;
      famRaw[fam].q26 += qty; famRaw[fam].v26 += vliq; famRaw[fam].c26 += custo; famRaw[fam].m26 += marg;
      artRaw[art].q26 += qty; artRaw[art].v26 += vliq; artRaw[art].c26 += custo; artRaw[art].m26 += marg;
      cliRaw[ent].q26 += qty; cliRaw[ent].v26 += vliq; cliRaw[ent].m26 += marg;
      kv26 += vliq; km26 += marg;
      ents26.add(ent); arts26.add(art);
      if (doc) docs26.add(doc);
    } else {
      aD.q25 += qty; aD.v25 += vliq; aD.m25 += marg;
      famRaw[fam].q25 += qty; famRaw[fam].v25 += vliq; famRaw[fam].c25 += custo; famRaw[fam].m25 += marg;
      artRaw[art].q25 += qty; artRaw[art].v25 += vliq; artRaw[art].c25 += custo; artRaw[art].m25 += marg;
      cliRaw[ent].q25 += qty; cliRaw[ent].v25 += vliq; cliRaw[ent].m25 += marg;
      kv25 += vliq; km25 += marg;
      ents25.add(ent); arts25.add(art);
      if (doc) docs25.add(doc);
      if (jsDate) {
        const ds = jsDate.toISOString().slice(0, 10);
        if (!lastDate[ent] || ds > lastDate[ent]) lastDate[ent] = ds;
      }
    }
    aD.cr = custo;

    // pais
    const paisIso = PAIS_COL ? (row[PAIS_COL] || '').toString().trim().toUpperCase() : null;
    if (paisIso) {
      if (!paisRaw[paisIso]) paisRaw[paisIso] = { v26:0,v25:0,m26:0,m25:0, ents26:new Set(), ents25:new Set() };
      const pd = paisRaw[paisIso];
      if (year === 2026) { pd.v26 += vliq; pd.m26 += marg; pd.ents26.add(ent); }
      else               { pd.v25 += vliq; pd.m25 += marg; pd.ents25.add(ent); }
    }
    // vendedor
    const vendCode = VEND_COL ? (row[VEND_COL] || '').toString().trim() : null;
    const vendNome = VEND_NOME_COL ? (row[VEND_NOME_COL] || '').toString().trim() : '';
    if (vendCode) {
      if (!vendRaw[vendCode]) vendRaw[vendCode] = { nome:vendNome||vendCode, v26:0,v25:0,m26:0,m25:0, ents26:new Set(), ents25:new Set(), docs26:new Set(), docs25:new Set() };
      const vd = vendRaw[vendCode];
      if (vendNome) vd.nome = vendNome;
      if (year === 2026) { vd.v26 += vliq; vd.m26 += marg; vd.ents26.add(ent); if (doc) vd.docs26.add(doc); }
      else               { vd.v25 += vliq; vd.m25 += marg; vd.ents25.add(ent); if (doc) vd.docs25.add(doc); }
    }

    n++;
  }

  // ─── passagem 2: drill ────────────────────────────────────────────────────
  const drill = {};
  for (const [fam, clients] of Object.entries(raw)) {
    drill[fam] = Object.values(clients).map(cli => {
      const items = Object.values(cli.arts).map(a => {
        const pm26 = a.v26 !== 0 ? (a.m26 / a.v26) * 100 : 0;
        const pm25 = a.v25 !== 0 ? (a.m25 / a.v25) * 100 : 0;
        const vp   = a.v25 !== 0 ? ((a.v26 - a.v25) / Math.abs(a.v25)) * 100 : (a.v26 !== 0 ? 100 : 0);
        return {
          a: a.a, d: a.d,
          q26: Math.round(a.q26), q25: Math.round(a.q25),
          v26: Math.round(a.v26), v25: Math.round(a.v25),
          pvu26: a.q26 !== 0 ? r2(a.v26 / a.q26) : 0,
          pvu25: a.q25 !== 0 ? r2(a.v25 / a.q25) : 0,
          cr: r2(a.cr),
          pm26: r1(pm26), pm25: r1(pm25),
          vm: r1(pm26 - pm25), vp: r1(vp),
        };
      }).sort((x, y) => y.v26 - x.v26);
      const v26 = items.reduce((s, i) => s + i.v26, 0);
      const v25 = items.reduce((s, i) => s + i.v25, 0);
      const m26 = items.reduce((s, i) => s + (i.v26 * i.pm26 / 100), 0);
      const m25 = items.reduce((s, i) => s + (i.v25 * i.pm25 / 100), 0);
      const pm26c = v26 !== 0 ? (m26 / v26) * 100 : 0;
      const pm25c = v25 !== 0 ? (m25 / v25) * 100 : 0;
      const vp    = v25 !== 0 ? ((v26 - v25) / Math.abs(v25)) * 100 : (v26 !== 0 ? 100 : 0);
      return {
        e: cli.e, n: cli.n,
        v26: Math.round(v26), v25: Math.round(v25),
        ve: Math.round(v26 - v25), vp: r1(vp),
        pm26: r1(pm26c), pm25: r1(pm25c), vm: r1(pm26c - pm25c),
        items,
      };
    }).sort((a, b) => b.v26 - a.v26);
  }

  // ─── passagem 3: vpData ───────────────────────────────────────────────────
  // kpis — nd/tk via documentos únicos se disponível, caso contrário omitidos
  const nd26 = docs26.size || 0;
  const nd25 = docs25.size || 0;
  const kpis = {
    v26: r2(kv26), v25: r2(kv25),
    pm26: pct(km26, kv26), pm25: pct(km25, kv25),
    nd26, nd25,
    nc26: ents26.size, nc25: ents25.size,
    na26: arts26.size, na25: arts25.size,
    tk26: nd26 ? r2(kv26 / nd26) : (ents26.size ? r2(kv26 / ents26.size) : 0),
    tk25: nd25 ? r2(kv25 / nd25) : (ents25.size ? r2(kv25 / ents25.size) : 0),
  };

  // monthly (usa maxM26 da pré-leitura)
  const vpMonthly = {};
  for (let m = 1; m <= 12; m++) {
    vpMonthly[m] = { lb: MONTH_LBL[m], y25: r2(monthly[m].y25), y26: r2(monthly[m].y26), ...(m === maxM26 ? { partial: true } : {}) };
  }

  // fam
  const vpFam = Object.values(famRaw).map(f => ({
    f: f.f,
    q26: Math.round(f.q26), q25: Math.round(f.q25),
    v26: r2(f.v26), v25: r2(f.v25), vv: vvar(f.v26, f.v25),
    c26: r2(f.c26), c25: r2(f.c25),
    pm26: pct(f.m26, f.v26), pm25: pct(f.m25, f.v25),
    vm: r1(pct(f.m26, f.v26) - pct(f.m25, f.v25)),
    vma: r2(f.m26 - f.m25),
  })).sort((a, b) => b.v26 - a.v26);

  // art (top 200)
  const vpArt = Object.values(artRaw).map(a => ({
    a: a.a, d: a.d, f: a.f,
    q26: Math.round(a.q26), q25: Math.round(a.q25),
    v26: r2(a.v26), v25: r2(a.v25), vv: vvar(a.v26, a.v25),
    pm26: pct(a.m26, a.v26), pm25: pct(a.m25, a.v25),
    vm: r1(pct(a.m26, a.v26) - pct(a.m25, a.v25)),
    pvu26: a.q26 ? r2(a.v26 / a.q26) : 0,
    pvu25: a.q25 ? r2(a.v25 / a.q25) : 0,
    cmu26: a.q26 ? r2(a.c26 / a.q26) : 0,
    cmu25: a.q25 ? r2(a.c25 / a.q25) : 0,
    cp:    a.q26 ? r2(a.c26 / a.q26) : 0,
  })).sort((a, b) => b.v26 - a.v26).slice(0, 200);

  // cli (all clients, top 200 for table)
  const allCli = Object.values(cliRaw).map(c => ({
    e: c.e, n: c.n,
    q26: Math.round(c.q26), q25: Math.round(c.q25),
    v26: r2(c.v26), v25: r2(c.v25), vv: vvar(c.v26, c.v25),
    pm26: pct(c.m26, c.v26), pm25: pct(c.m25, c.v25),
    vm: r1(pct(c.m26, c.v26) - pct(c.m25, c.v25)),
  }));
  const vpCli = [...allCli].sort((a, b) => b.v26 - a.v26).slice(0, 200);

  // perdidos: tinham 2025 mas não têm 2026
  const vpPerdidos = allCli
    .filter(c => c.v25 > 0 && c.v26 === 0)
    .sort((a, b) => b.v25 - a.v25).slice(0, 20)
    .map(c => ({ n: c.n, v25: Math.round(c.v25), last: lastDate[c.e] || '—' }));

  // queda: >30% de declínio (não total)
  const vpQueda = allCli
    .filter(c => c.v25 > 0 && c.v26 > 0 && c.vv < -30)
    .sort((a, b) => b.v25 - a.v25).slice(0, 20)
    .map(c => ({ n: c.n, vp: Math.round(c.vv), risco: Math.round(c.v25 - c.v26) }));

  // novos: sem 2025, com 2026
  const vpNovos = allCli
    .filter(c => c.v25 === 0 && c.v26 > 0)
    .sort((a, b) => b.v26 - a.v26).slice(0, 20)
    .map(c => ({ n: c.n, v26: Math.round(c.v26) }));

  // crescimento: >30% (excluindo novos)
  const vpCrescimento = allCli
    .filter(c => c.v25 > 0 && c.v26 > 0 && c.vv > 30)
    .sort((a, b) => (b.v26 - b.v25) - (a.v26 - a.v25)).slice(0, 20)
    .map(c => ({ n: c.n, vp: Math.round(c.vv), ve: Math.round(c.v26 - c.v25) }));

  // caRisk: artigos-cliente com queda > 40%, v25 >= 5000
  const vpCaRisk = [];
  for (const [, clients] of Object.entries(drill)) {
    for (const cli of clients) {
      for (const item of cli.items) {
        if (item.v25 >= 5000 && item.vp < -40) {
          const risco = item.vp < -90 ? 'Perdido' : item.vp < -60 ? 'Risco alto' : 'Redução';
          vpCaRisk.push({ e: cli.e, n: cli.n, a: item.a, ad: item.d, q25: item.q25, q26: item.q26, v25: item.v25, v26: item.v26, vp: item.vp, risco });
        }
      }
    }
  }
  vpCaRisk.sort((a, b) => b.v25 - a.v25);

  // caGrowth: clientes com crescimento > 40% e v25 >= 5000
  const vpCaGrowth = allCli
    .filter(c => c.v25 >= 5000 && c.vv > 40)
    .sort((a, b) => (b.v26 - b.v25) - (a.v26 - a.v25)).slice(0, 20)
    .map(c => {
      const cliItems = [];
      for (const clients of Object.values(drill)) {
        const entry = clients.find(x => x.e === c.e);
        if (entry) cliItems.push(...entry.items);
      }
      cliItems.sort((a, b) => b.v26 - a.v26);
      return {
        e: c.e, n: c.n, v25: c.v25, v26: c.v26,
        ve: Math.round(c.v26 - c.v25), vp: c.vv,
        arts: cliItems.length,
        novos: cliItems.filter(i => i.v25 === 0 && i.v26 > 0).length,
        alto:  cliItems.filter(i => i.vp > 100).length,
        cresc: cliItems.filter(i => i.vp > 0 && i.vp <= 100).length,
        items: cliItems.slice(0, 15).map(i => ({
          a: i.a, ad: i.d,
          q25: i.q25, q26: i.q26, v25: i.v25, v26: i.v26, vp: i.vp,
          tipo: i.v25 === 0 ? 'Novo' : i.vp > 100 ? 'Crescimento alto' : i.vp > 0 ? 'Crescimento' : 'Queda',
        })),
      };
    });

  // grow / decline: top 10 artigos por variação
  const artList = Object.values(artRaw).filter(a => a.v25 > 500 || a.v26 > 500);
  const vpGrow = artList
    .filter(a => a.v25 > 0 && a.v26 > 0)
    .map(a => ({ a: a.a, v26: Math.round(a.v26), v25: Math.round(a.v25), vp: Math.round(vvar(a.v26, a.v25)) }))
    .sort((a, b) => b.vp - a.vp).slice(0, 10);
  const vpDecline = artList
    .map(a => ({ a: a.a, v26: Math.round(a.v26), v25: Math.round(a.v25), vp: Math.round(vvar(a.v26, a.v25)) }))
    .filter(a => a.vp < 0)
    .sort((a, b) => a.vp - b.vp).slice(0, 10);

  // paises
  const vpPaises = Object.entries(paisRaw).map(([iso, p]) => ({
    p: _ISO_NOME[iso] || iso,
    iso,
    v26: r2(p.v26), v25: r2(p.v25),
    ve: r2(p.v26 - p.v25),
    vv: vvar(p.v26, p.v25),
    mg26: pct(p.m26, p.v26),
    mg25: pct(p.m25, p.v25),
    nc26: p.ents26.size,
    nc25: p.ents25.size,
  })).sort((a, b) => b.v26 - a.v26);

  // vendedores + grupos
  const vpVendedores = Object.values(vendRaw).map(v => {
    const grupo = _GRUPO_INFER(v.nome);
    const nd26 = v.docs26.size, nd25 = v.docs25.size;
    return {
      nome: v.nome, grupo,
      v26: r2(v.v26), v25: r2(v.v25),
      pm26: pct(v.m26, v.v26), pm25: pct(v.m25, v.v25),
      vm: r1(pct(v.m26, v.v26) - pct(v.m25, v.v25)),
      nd26, nd25,
      nc26: v.ents26.size, nc25: v.ents25.size,
      vv: vvar(v.v26, v.v25),
    };
  }).sort((a, b) => b.v26 - a.v26);

  const grupoMap = {};
  for (const v of vpVendedores) {
    if (!grupoMap[v.grupo]) grupoMap[v.grupo] = { grupo: v.grupo, v26:0, v25:0, m26:0, m25:0, vendedores:0 };
    const g = grupoMap[v.grupo];
    g.v26 += v.v26; g.v25 += v.v25;
    g.m26 += v.v26 * v.pm26 / 100; g.m25 += v.v25 * v.pm25 / 100;
    g.vendedores++;
  }
  const GRUPO_ORDER = ['Export','Nacional','Grupo','Partners','Sensek','Mimaki','Sede','Outros'];
  const vpGrupos = Object.values(grupoMap).map(g => ({
    grupo: g.grupo,
    v26: r2(g.v26), v25: r2(g.v25),
    pm26: pct(g.m26, g.v26), pm25: pct(g.m25, g.v25),
    vv: vvar(g.v26, g.v25),
    vm: r1(pct(g.m26, g.v26) - pct(g.m25, g.v25)),
    vendedores: g.vendedores,
  })).sort((a, b) => (GRUPO_ORDER.indexOf(a.grupo) + 1 || 99) - (GRUPO_ORDER.indexOf(b.grupo) + 1 || 99));

  // retencao: clientes retidos por vendedor (nc26, nc25, ret = interseção)
  const vpRetencao = Object.values(vendRaw)
    .filter(vd => vd.ents25.size > 0 || vd.ents26.size > 0)
    .map(vd => ({
      nome: vd.nome,
      grupo: _GRUPO_INFER(vd.nome),
      nc26: vd.ents26.size,
      nc25: vd.ents25.size,
      ret: [...vd.ents26].filter(e => vd.ents25.has(e)).length,
    }))
    .sort((a, b) => b.nc26 - a.nc26);

  const vpData = {
    kpis, monthly: vpMonthly, fam: vpFam, art: vpArt, cli: vpCli,
    perdidos: vpPerdidos, queda: vpQueda, novos: vpNovos, crescimento: vpCrescimento,
    caRisk: vpCaRisk, caGrowth: vpCaGrowth,
    grow: vpGrow, decline: vpDecline,
    grupos: vpGrupos, paises: vpPaises, retencao: vpRetencao, vendedores: vpVendedores,
  };

  return { drill, vpData, rowsProcessed: n };
}

// ── Parser de stocks → resumo por família ─────────────────────────────────
function processStocks(rows) {
  const byFam = {};
  for (const row of rows) {
    const fam = (row['Família (Descr.)'] || row['Família (Descrição)'] || '').trim();
    if (!fam) continue;
    if (!byFam[fam]) byFam[fam] = { fam, qty: 0, val: 0, arts: 0 };
    byFam[fam].qty  += Number(row['Stock']) || 0;
    byFam[fam].val  += Number(row['Existências (Val.) (EUR)']) || 0;
    byFam[fam].arts += 1;
  }
  return Object.values(byFam).sort((a, b) => b.val - a.val);
}

// Extrai data do nome do ficheiro (DDMMYYYY, DDMMYY, DD-MM-YYYY, YYYY-MM-DD)
function extractFileDate(filename) {
  // DDMMYYYY (8 dígitos): e.g. 17052026
  let m = filename.match(/\b(\d{2})(\d{2})(\d{4})\b/);
  if (m) return `${m[1]}/${m[2]}/${m[3]}`;
  // DD-MM-YYYY com separadores
  m = filename.match(/\b(\d{2})[\/\-_](\d{2})[\/\-_](\d{4})\b/);
  if (m) return `${m[1]}/${m[2]}/${m[3]}`;
  // YYYY-MM-DD ISO
  m = filename.match(/\b(\d{4})[\/\-_](\d{2})[\/\-_](\d{2})\b/);
  if (m) return `${m[3]}/${m[2]}/${m[1]}`;
  // DDMMYY (6 dígitos): e.g. 170526 → 17/05/2026
  m = filename.match(/\b(\d{2})(\d{2})(\d{2})\b/);
  if (m) return `${m[1]}/${m[2]}/20${m[3]}`;
  return null;
}

// ── Drop zone ──────────────────────────────────────────────────────────────
const DropZone = ({ label, accept, file, onFile, disabled }) => {
  const [over, setOver] = React.useState(false);
  const ref = React.useRef();
  const onDrop = (e) => {
    e.preventDefault(); setOver(false);
    const f = e.dataTransfer?.files?.[0] || e.target.files?.[0];
    if (f) onFile(f);
  };
  return (
    <div
      onClick={() => !disabled && ref.current?.click()}
      onDragOver={e => { e.preventDefault(); setOver(true); }}
      onDragLeave={() => setOver(false)}
      onDrop={onDrop}
      style={{
        border: `2px dashed ${over ? 'var(--brand-500)' : file ? 'oklch(0.65 0.18 145)' : 'var(--border)'}`,
        borderRadius: 10, padding: '22px 20px', textAlign: 'center', cursor: disabled ? 'default' : 'pointer',
        background: over ? 'color-mix(in oklch, var(--brand-500) 6%, transparent)' : file ? 'color-mix(in oklch, oklch(0.65 0.18 145) 6%, transparent)' : 'var(--bg-elev)',
        transition: 'all 0.15s',
      }}
    >
      <input ref={ref} type="file" accept={accept} style={{ display: 'none' }} onChange={onDrop} />
      <div style={{ fontSize: 22, marginBottom: 6 }}>{file ? '✓' : '↑'}</div>
      <div style={{ fontSize: 13, fontWeight: 600, color: file ? 'oklch(0.55 0.18 145)' : 'var(--text)' }}>
        {file ? file.name : label}
      </div>
      <div style={{ fontSize: 11, color: 'var(--text-dim)', marginTop: 4 }}>
        {file ? `${(file.size / 1024 / 1024).toFixed(1)} MB` : 'Arrasta ou clica para seleccionar .xlsx'}
      </div>
    </div>
  );
};

// ── Screen principal ───────────────────────────────────────────────────────
const AdminImportScreen = () => {
  const [vendasFile,  setVendasFile]  = React.useState(null);
  const [stocksFile,  setStocksFile]  = React.useState(null);
  const [status,      setStatus]      = React.useState('idle'); // idle|loading-lib|parsing|processing|done|error
  const [progress,    setProgress]    = React.useState('');
  const [result,      setResult]      = React.useState(null);
  const [error,       setError]       = React.useState(null);

  const canProcess = vendasFile && stocksFile && status === 'idle';

  // Lê o timestamp guardado (se existir)
  const savedTs = React.useMemo(() => {
    try { return localStorage.getItem('vp-drill-ts') || null; } catch { return null; }
  }, []);

  const handleProcess = async () => {
    setStatus('loading-lib');
    setProgress('A carregar SheetJS...');
    setError(null);
    try {
      const XLSX = await loadSheetJS();

      // Parse ficheiro de vendas
      setStatus('parsing');
      setProgress('A ler vendas.xlsx... (ficheiro grande, pode demorar 10-20s)');
      await new Promise(r => setTimeout(r, 50)); // yield para UI actualizar

      const readFile = (f) => new Promise((res, rej) => {
        const reader = new FileReader();
        reader.onload = e => res(e.target.result);
        reader.onerror = rej;
        reader.readAsArrayBuffer(f);
      });

      const [vendasBuf, stocksBuf] = await Promise.all([readFile(vendasFile), readFile(stocksFile)]);

      setProgress('A fazer parse de vendas.xlsx...');
      await new Promise(r => setTimeout(r, 50));
      const wbVendas = XLSX.read(vendasBuf, { type: 'array', cellDates: true });
      const wsVendas = wbVendas.Sheets[wbVendas.SheetNames[0]];
      const rowsVendas = XLSX.utils.sheet_to_json(wsVendas, { defval: null });

      setProgress('A fazer parse de stocks.xlsx...');
      await new Promise(r => setTimeout(r, 50));
      const wbStocks = XLSX.read(stocksBuf, { type: 'array', cellDates: true });
      const wsStocks = wbStocks.Sheets[wbStocks.SheetNames[0]];
      const rowsStocks = XLSX.utils.sheet_to_json(wsStocks, { defval: null });

      // Processar
      setStatus('processing');
      setProgress('A processar ' + rowsVendas.length.toLocaleString('pt-PT') + ' linhas de vendas...');
      await new Promise(r => setTimeout(r, 80));

      const { drill, vpData, rowsProcessed } = processVendas(rowsVendas);
      const stocksSummary = processStocks(rowsStocks);

      const famCount = Object.keys(drill).length;
      const cliCount = Object.values(drill).reduce((s, arr) => s + arr.length, 0);
      const artCount = Object.values(drill).reduce((s, arr) => arr.reduce((acc, c) => acc + c.items.length, s), 0);
      const ts       = new Date().toISOString();

      // Guardar resultado em window + localStorage
      window._VP_DRILL = drill;
      window._VP_DATA  = vpData;
      const fileDate = extractFileDate(vendasFile.name);
      try {
        localStorage.setItem('vp-drill-ts',   ts);
        localStorage.setItem('vp-file-date',  fileDate || '');
        localStorage.setItem('vp-drill-data', JSON.stringify(drill));
        localStorage.setItem('vp-data',       JSON.stringify(vpData));
      } catch (e) {
        console.warn('[Import] localStorage cheio — dados só em memória', e);
      }
      // Notifica o ecrã de Vendas & Performance para re-renderizar com dados novos
      window.dispatchEvent(new CustomEvent('vp-data-updated'));

      setResult({ famCount, cliCount, artCount, rowsProcessed, ts, stocksSummary: stocksSummary.slice(0, 10) });
      setStatus('done');
    } catch (e) {
      console.error('[Import]', e);
      setError(e.message || String(e));
      setStatus('error');
    }
  };

  const handleReset = () => {
    setStatus('idle'); setResult(null); setError(null); setProgress('');
    setVendasFile(null); setStocksFile(null);
  };

  const isProcessing = ['loading-lib','parsing','processing'].includes(status);

  return (
    <div style={{ flex: 1, overflow: 'auto', padding: '28px 32px' }}>
      <div style={{ maxWidth: 860, margin: '0 auto' }}>
        {/* Header */}
        <div style={{ fontSize: 10, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', letterSpacing: '0.08em' }}>ADMIN · IMPORTAR DADOS</div>
        <h1 className="font-display" style={{ margin: '6px 0 4px', fontSize: 26, fontWeight: 600 }}>Importar Dados Primavera</h1>
        <p style={{ margin: '0 0 24px', fontSize: 13.5, color: 'var(--text-muted)', lineHeight: 1.55, maxWidth: 640 }}>
          Carrega os dois ficheiros exportados pelo Márcio do Primavera. O sistema processa as vendas e gera os dados de drill-down para o dashboard Vendas &amp; Performance.
        </p>

        {savedTs && status === 'idle' && (
          <div style={{ marginBottom: 20, padding: '10px 14px', borderRadius: 8, background: 'color-mix(in oklch, oklch(0.65 0.18 145) 10%, transparent)', border: '1px solid color-mix(in oklch, oklch(0.65 0.18 145) 30%, transparent)', fontSize: 12, color: 'oklch(0.45 0.18 145)' }}>
            Última importação: {new Date(savedTs).toLocaleString('pt-PT')} — dados activos em memória se a página não foi recarregada.
          </div>
        )}

        {/* Upload zones */}
        {status === 'idle' || status === 'error' ? (
          <>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, marginBottom: 20 }}>
              <div>
                <div style={{ fontSize: 11, fontWeight: 600, color: 'var(--text-muted)', marginBottom: 6, fontFamily: 'var(--font-mono)', letterSpacing: '0.04em' }}>VENDAS (~25 MB)</div>
                <DropZone label="vendas decal Digi AI …xlsx" accept=".xlsx" file={vendasFile} onFile={setVendasFile} />
              </div>
              <div>
                <div style={{ fontSize: 11, fontWeight: 600, color: 'var(--text-muted)', marginBottom: 6, fontFamily: 'var(--font-mono)', letterSpacing: '0.04em' }}>STOCKS (~2 MB)</div>
                <DropZone label="stocks decal Digi AI …xlsx" accept=".xlsx" file={stocksFile} onFile={setStocksFile} />
              </div>
            </div>

            {error && (
              <div style={{ marginBottom: 16, padding: '10px 14px', borderRadius: 8, background: 'color-mix(in oklch, #dc2626 10%, transparent)', border: '1px solid color-mix(in oklch, #dc2626 30%, transparent)', fontSize: 12, color: '#dc2626' }}>
                Erro: {error}
              </div>
            )}

            <button
              onClick={handleProcess}
              disabled={!canProcess}
              style={{
                padding: '10px 24px', borderRadius: 8, fontSize: 13.5, fontWeight: 600,
                background: canProcess ? 'var(--brand-600, oklch(0.55 0.2 255))' : 'var(--surface-muted)',
                color: canProcess ? '#fff' : 'var(--text-dim)',
                border: 'none', cursor: canProcess ? 'pointer' : 'not-allowed',
                transition: 'opacity 0.15s',
              }}
            >
              Processar ficheiros
            </button>
            <div style={{ marginTop: 10, fontSize: 11.5, color: 'var(--text-dim)' }}>
              O processamento pode demorar 15–30 segundos para 200k+ linhas.
            </div>
          </>
        ) : isProcessing ? (
          /* Processing state */
          <div style={{ padding: '40px 0', textAlign: 'center' }}>
            <div style={{ width: 40, height: 40, margin: '0 auto 16px', borderRadius: '50%', border: '3px solid var(--brand-500)', borderTopColor: 'transparent', animation: 'spin 0.8s linear infinite' }} />
            <div style={{ fontSize: 14, fontWeight: 600, marginBottom: 6 }}>A processar…</div>
            <div style={{ fontSize: 12, color: 'var(--text-muted)' }}>{progress}</div>
            <style>{`@keyframes spin { to { transform: rotate(360deg); } }`}</style>
          </div>
        ) : status === 'done' && result ? (
          /* Result */
          <>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 12, marginBottom: 24 }}>
              {[
                { label: 'Famílias', val: result.famCount.toLocaleString('pt-PT') },
                { label: 'Clientes', val: result.cliCount.toLocaleString('pt-PT') },
                { label: 'Artigos', val: result.artCount.toLocaleString('pt-PT') },
                { label: 'Linhas processadas', val: result.rowsProcessed.toLocaleString('pt-PT') },
              ].map(({ label, val }) => (
                <div key={label} style={{ padding: '14px 16px', borderRadius: 9, border: '1px solid var(--border)', background: 'var(--bg-elev)' }}>
                  <div style={{ fontSize: 11, color: 'var(--text-dim)', marginBottom: 4 }}>{label}</div>
                  <div style={{ fontSize: 22, fontWeight: 700 }}>{val}</div>
                </div>
              ))}
            </div>

            <div style={{ marginBottom: 20, padding: '12px 16px', borderRadius: 8, background: 'color-mix(in oklch, oklch(0.65 0.18 145) 8%, transparent)', border: '1px solid color-mix(in oklch, oklch(0.65 0.18 145) 25%, transparent)', fontSize: 12.5, color: 'oklch(0.45 0.18 145)', fontWeight: 500 }}>
              ✓ window._VP_DRILL actualizado — os dados já estão activos. Navega para Comercial → Vendas &amp; Performance para ver o resultado.
            </div>

            {/* Top stocks por valor */}
            {result.stocksSummary.length > 0 && (
              <div style={{ marginBottom: 24 }}>
                <div style={{ fontSize: 11, fontWeight: 600, color: 'var(--text-muted)', marginBottom: 8, fontFamily: 'var(--font-mono)', letterSpacing: '0.04em' }}>TOP 10 FAMÍLIAS POR VALOR DE STOCK</div>
                <div style={{ borderRadius: 8, border: '1px solid var(--border)', overflow: 'hidden' }}>
                  {result.stocksSummary.map((s, i) => (
                    <div key={s.fam} style={{ display: 'grid', gridTemplateColumns: '28px 1fr 80px 80px 60px', alignItems: 'center', gap: 12, padding: '9px 14px', fontSize: 12, borderBottom: i < result.stocksSummary.length - 1 ? '1px solid var(--border)' : 'none', background: 'var(--bg-elev)' }}>
                      <span style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--text-dim)' }}>{i + 1}</span>
                      <span style={{ fontWeight: 500 }}>{s.fam}</span>
                      <span style={{ textAlign: 'right', fontFamily: 'var(--font-mono)', fontSize: 11 }}>{Math.round(s.val).toLocaleString('pt-PT')} €</span>
                      <span style={{ textAlign: 'right', fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--text-muted)' }}>{Math.round(s.qty).toLocaleString('pt-PT')} un</span>
                      <span style={{ textAlign: 'right', fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--text-dim)' }}>{s.arts} art.</span>
                    </div>
                  ))}
                </div>
              </div>
            )}

            <div style={{ display: 'flex', gap: 10 }}>
              <button onClick={handleReset} style={{ padding: '8px 18px', borderRadius: 7, fontSize: 13, fontWeight: 500, border: '1px solid var(--border)', background: 'var(--bg-elev)', color: 'var(--text)', cursor: 'pointer' }}>
                Nova importação
              </button>
            </div>
          </>
        ) : null}

        {/* Info box */}
        <div style={{ marginTop: 28, padding: '14px 16px', borderRadius: 9, border: '1px dashed var(--border)', background: 'var(--surface-muted)', fontSize: 12, color: 'var(--text-muted)', lineHeight: 1.6 }}>
          <strong style={{ color: 'var(--text)' }}>Como funciona:</strong> os dados são processados no browser e guardados em localStorage. Ao recarregar a página os dados são reposto de localStorage automaticamente. Para persistência no servidor será necessária integração directa com o Primavera (fase 2).
        </div>
      </div>
    </div>
  );
};

// Restore from localStorage on load
(function restoreVpDrill() {
  try {
    const saved = localStorage.getItem('vp-drill-data');
    if (saved && !window._VP_DRILL) {
      window._VP_DRILL = JSON.parse(saved);
      console.log('[Import] _VP_DRILL restaurado de localStorage');
    }
  } catch (e) { console.warn('[Import] falha ao restaurar _VP_DRILL', e); }
})();

window.AdminImportScreen = AdminImportScreen;
