/* Mock data: redes, transações, adquirentes, taxas, planos, regras, eventos */

// Deterministic PRNG for stable mocks
function mkRand(seed = 42) {
  let s = seed;
  return () => {
    s = (s * 9301 + 49297) % 233280;
    return s / 233280;
  };
}
const rand = mkRand(7);
const pick = (arr) => arr[Math.floor(rand() * arr.length)];
const range = (n) => Array.from({ length: n }, (_, i) => i);
const pad = (n, w = 2) => String(n).padStart(w, '0');
const fmtBRL = (v) =>
  'R$ ' + (Number(v) || 0).toLocaleString('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
const fmtNum = (v) => (Number(v) || 0).toLocaleString('pt-BR');
const fmtPct = (v, d = 2) => (Number(v) || 0).toFixed(d).replace('.', ',') + '%';
const fmtDate = (d) => {
  const dt = typeof d === 'string' ? new Date(d) : d;
  return pad(dt.getDate()) + '/' + pad(dt.getMonth() + 1) + '/' + dt.getFullYear();
};
const fmtDateTime = (d) => {
  const dt = typeof d === 'string' ? new Date(d) : d;
  return fmtDate(dt) + ' ' + pad(dt.getHours()) + ':' + pad(dt.getMinutes());
};
const daysAgo = (n) => {
  const d = new Date();
  d.setDate(d.getDate() - n);
  return d;
};
const todayISO = () => new Date().toISOString();

// ---------- Redes / Filiais / Unidades ----------
const REDES = [
  { id: 'r1', nome: 'Rede Fiolaser Fitness', cnpj: '12.345.678/0001-90' },
  { id: 'r2', nome: 'BodyTech Group', cnpj: '23.456.789/0001-01' },
  { id: 'r3', nome: 'Smart Fit Brasil', cnpj: '34.567.890/0001-12' },
  { id: 'r4', nome: 'Companhia Athletica', cnpj: '45.678.901/0001-23' },
  { id: 'r5', nome: 'Cia da Saúde', cnpj: '56.789.012/0001-34' },
];
const CIDADES = ['São Paulo','Rio de Janeiro','Belo Horizonte','Curitiba','Porto Alegre','Salvador','Recife','Fortaleza','Brasília','Florianópolis','Campinas'];
const BAIRROS = ['Pinheiros','Vila Olímpia','Moema','Itaim','Tijuca','Botafogo','Centro','Jardins','Savassi','Batel'];

function genUnidades() {
  const out = [];
  REDES.forEach((rede, ri) => {
    const nFiliais = 2 + Math.floor(rand() * 3);
    range(nFiliais).forEach((fi) => {
      const filialId = `${rede.id}-f${fi + 1}`;
      const filialNome = `${rede.nome} – Regional ${['SP','RJ','MG','SUL','NE'][fi % 5]}`;
      const nUnidades = 1 + Math.floor(rand() * 3);
      range(nUnidades).forEach((ui) => {
        const cidade = pick(CIDADES);
        const bairro = pick(BAIRROS);
        out.push({
          id: `${filialId}-u${ui + 1}`,
          rede: rede.id,
          redeNome: rede.nome,
          filial: filialId,
          filialNome,
          nome: `${rede.nome.split(' ')[0]} ${bairro}`,
          cidade,
          uf: ['SP','RJ','MG','PR','RS','BA','PE','CE','DF','SC'][CIDADES.indexOf(cidade) % 10] || 'SP',
          cnpj: pad(10 + ri, 2) + '.' + pad(100 + fi * 10 + ui, 3) + '.' + pad(ui + 1, 3) + '/0001-' + pad(10 + ui, 2),
          mcc: pick(['7997','7298','7991']),
          status: rand() > 0.08 ? 'ativo' : (rand() > 0.5 ? 'pendente' : 'suspenso'),
          plano: pick(['Bronze','Prata','Ouro','Diamante']),
          adquirentePref: pick(['cielo','rede','getnet']),
          volume30d: 30000 + Math.floor(rand() * 220000),
          alunos: 200 + Math.floor(rand() * 1800),
          criadoEm: daysAgo(30 + Math.floor(rand() * 600)).toISOString(),
        });
      });
    });
  });
  return out;
}
const UNIDADES = genUnidades();
const FILIAIS = Array.from(new Set(UNIDADES.map(u => u.filial))).map(fid => {
  const u = UNIDADES.find(x => x.filial === fid);
  return { id: fid, nome: u.filialNome, rede: u.rede, unidades: UNIDADES.filter(x => x.filial === fid).length };
});

// ---------- Adquirentes ----------
const ADQUIRENTES = [
  { id: 'cielo', nome: 'Cielo', cor: '#0099CC', status: 'ok', aprov24h: 96.3, msResp: 412, ultTx: '12s atrás', share: 41 },
  { id: 'rede',  nome: 'Rede',  cor: '#E60000', status: 'warn', aprov24h: 94.1, msResp: 538, ultTx: '4s atrás', share: 33 },
  { id: 'getnet',nome: 'GetNet',cor: '#EC3237', status: 'ok', aprov24h: 95.7, msResp: 467, ultTx: '8s atrás', share: 26 },
];

// ---------- Bandeiras ----------
const BANDEIRAS = [
  { id: 'visa', nome: 'Visa', cor: '#1A1F71' },
  { id: 'master', nome: 'Mastercard', cor: '#EB001B' },
  { id: 'amex', nome: 'Amex', cor: '#006FCF' },
  { id: 'elo', nome: 'Elo', cor: '#00A4E0' },
  { id: 'hiper', nome: 'Hipercard', cor: '#822124' },
  { id: 'diners', nome: 'Diners', cor: '#0079BE' },
  { id: 'discover', nome: 'Discover', cor: '#FF6000' },
  { id: 'jcb', nome: 'JCB', cor: '#0E4C96' },
  { id: 'aura', nome: 'Aura', cor: '#B11116' },
];

// ---------- Taxas adquirentes ----------
// bandeira × faixa × adquirente
const FAIXAS = [
  { id: 'cred1', nome: 'Crédito 1x' },
  { id: 'cred2_6', nome: 'Crédito 2-6x' },
  { id: 'cred7_12', nome: 'Crédito 7-12x' },
  { id: 'recorrente', nome: 'Recorrente' },
  { id: 'debito', nome: 'Débito' },
];

function genTaxasAdquirente() {
  const out = [];
  BANDEIRAS.forEach((b) => {
    FAIXAS.forEach((f) => {
      ADQUIRENTES.forEach((a) => {
        const base = {
          cred1: 1.6 + rand() * 1.4,
          cred2_6: 2.4 + rand() * 0.8,
          cred7_12: 3.0 + rand() * 0.6,
          recorrente: 2.1 + rand() * 0.5,
          debito: 0.9 + rand() * 0.4,
        }[f.id];
        out.push({
          bandeira: b.id,
          faixa: f.id,
          adquirente: a.id,
          taxa: +base.toFixed(2),
        });
      });
    });
  });
  return out;
}
const TAXAS_ADQUIRENTE = genTaxasAdquirente();

// ---------- Planos cliente (spread PACTO) ----------
const PLANOS = [
  { id: 'bronze', nome: 'Bronze', spread: 0.50, minVolume: 0, descricao: 'Plano de entrada para unidades novas' },
  { id: 'prata', nome: 'Prata', spread: 0.40, minVolume: 80000, descricao: 'A partir de R$80k/mês de volume' },
  { id: 'ouro', nome: 'Ouro', spread: 0.30, minVolume: 250000, descricao: 'A partir de R$250k/mês' },
  { id: 'diamante', nome: 'Diamante', spread: 0.20, minVolume: 600000, descricao: 'Redes grandes — negociado caso a caso' },
];

// ---------- Regras orquestrador ----------
const REGRAS_ORQUESTRADOR = [
  { id: 'g1', ordem: 1, nome: 'Roteamento por menor custo total', tipo: 'cost', ativa: true, descricao: 'Para qualquer transação, escolher a adquirente com (taxa adquirente + spread PACTO) menor. Empate técnico (<0.05pp): preferir a com maior aprovação 24h.' },
  { id: 'g2', ordem: 2, nome: 'Failover automático', tipo: 'failover', ativa: true, descricao: 'Se adquirente principal cair ou aprovação 24h < 85%, derivar para a segunda opção do ranking de custo.' },
  { id: 'g3', ordem: 3, nome: 'Amex sempre via Cielo', tipo: 'override', ativa: true, descricao: 'Bandeira Amex roteia apenas para Cielo (única com contrato direto Amex).' },
  { id: 'g4', ordem: 4, nome: 'Débito Elo via Rede', tipo: 'override', ativa: false, descricao: 'Negociação em andamento. Quando ativada, débito Elo passa a rotear via Rede.' },
  { id: 'g5', ordem: 5, nome: 'Recorrente preferencial GetNet', tipo: 'preference', ativa: true, descricao: 'Transações recorrentes preferem GetNet (melhor retry de cartões expirados).' },
];

// ---------- Transações ----------
const STATUS_TX = [
  { id: 'aprovada', label: 'Aprovada', cor: 'ok', peso: 90 },
  { id: 'pendente', label: 'Pendente', cor: 'warn', peso: 3 },
  { id: 'recusada', label: 'Recusada', cor: 'err', peso: 5 },
  { id: 'chargeback', label: 'Chargeback', cor: 'err', peso: 2 },
];
const MODALIDADES = [
  { id: 'cred1', nome: 'Crédito 1x', faixa: 'cred1' },
  { id: 'cred_parc', nome: 'Crédito Parcelado', faixa: 'cred2_6' },
  { id: 'recorrente', nome: 'Recorrente', faixa: 'recorrente' },
  { id: 'debito', nome: 'Débito', faixa: 'debito' },
  { id: 'pix', nome: 'PIX', faixa: 'pix' },
  { id: 'pix_rec', nome: 'PIX Recorrente', faixa: 'pix_rec' },
];

const FIRSTS = ['Lucas','Maria','João','Ana','Pedro','Camila','Rafael','Júlia','Felipe','Beatriz','Gustavo','Larissa','Bruno','Fernanda','Tiago','Carolina','Mateus','Isabela','Daniel','Patrícia'];
const LASTS  = ['Silva','Santos','Oliveira','Souza','Rodrigues','Ferreira','Almeida','Pereira','Lima','Gomes','Costa','Ribeiro','Carvalho','Martins','Araújo','Melo','Nascimento','Cardoso','Reis','Teixeira'];

function genTransacoes(n = 540) {
  const out = [];
  for (let i = 0; i < n; i++) {
    const minutesAgo = Math.floor(rand() * 60 * 24 * 60); // up to 60d
    const d = new Date();
    d.setMinutes(d.getMinutes() - minutesAgo);
    const u = UNIDADES[Math.floor(rand() * UNIDADES.length)];
    const r = rand() * 100;
    let status = 'aprovada';
    let acc = 0;
    for (const s of STATUS_TX) { acc += s.peso; if (r < acc) { status = s.id; break; } }
    const modal = MODALIDADES[Math.floor(rand() * MODALIDADES.length)];
    let bandeira = BANDEIRAS[Math.floor(rand() * BANDEIRAS.length)].id;
    if (modal.id === 'pix' || modal.id === 'pix_rec') bandeira = 'pix';
    const parcelas = modal.id === 'cred_parc' ? (2 + Math.floor(rand() * 11)) : 1;
    const valor = +(50 + rand() * 450).toFixed(2);
    // route by lowest cost
    const txFaixa = bandeira === 'pix' ? null :
      (modal.id === 'cred_parc' ? (parcelas <= 6 ? 'cred2_6' : 'cred7_12') : modal.faixa);
    let adq = 'cielo'; let taxaAdq = 0;
    if (txFaixa) {
      const opts = TAXAS_ADQUIRENTE.filter(t => t.bandeira === bandeira && t.faixa === txFaixa);
      if (opts.length) {
        const min = opts.reduce((a, b) => (a.taxa < b.taxa ? a : b));
        adq = min.adquirente; taxaAdq = min.taxa;
      }
    } else {
      adq = 'cielo'; taxaAdq = 0.99;
    }
    if (bandeira === 'amex') adq = 'cielo';
    const plano = PLANOS.find(p => p.nome === u.plano);
    const spread = plano ? plano.spread : 0.40;
    const taxaTotal = +(taxaAdq + spread).toFixed(2);
    const valorLiq = +(valor * (1 - taxaTotal / 100)).toFixed(2);
    const aluno = FIRSTS[Math.floor(rand() * FIRSTS.length)] + ' ' + LASTS[Math.floor(rand() * LASTS.length)];
    out.push({
      id: 'tx_' + (1000000 + i).toString(36).toUpperCase(),
      data: d.toISOString(),
      unidade: u.id, unidadeNome: u.nome, rede: u.rede, redeNome: u.redeNome, filial: u.filial,
      aluno,
      modalidade: modal.id, modalidadeNome: modal.nome,
      bandeira, parcelas,
      adquirente: adq, taxaAdquirente: taxaAdq, spread, taxaTotal,
      valor, valorLiquido: valorLiq, status,
      autorizacao: 'AUT' + Math.floor(rand() * 1e6).toString().padStart(6, '0'),
      nsu: Math.floor(rand() * 1e7).toString().padStart(7, '0'),
    });
  }
  return out.sort((a, b) => b.data.localeCompare(a.data));
}
const TRANSACOES = genTransacoes(540);

// ---------- Antecipações ----------
function genAntecipacoes(n = 42) {
  const out = [];
  for (let i = 0; i < n; i++) {
    const u = UNIDADES[Math.floor(rand() * UNIDADES.length)];
    const status = pick(['solicitada','solicitada','em_analise','aprovada','aprovada','rejeitada','paga']);
    const valor = +(8000 + rand() * 90000).toFixed(2);
    const taxa = +(2.2 + rand() * 1.6).toFixed(2);
    const liquido = +(valor * (1 - taxa / 100)).toFixed(2);
    out.push({
      id: 'ant_' + (300 + i),
      unidade: u.id, unidadeNome: u.nome, redeNome: u.redeNome,
      valor, taxa, liquido,
      criada: daysAgo(Math.floor(rand() * 20)).toISOString(),
      status,
      recebiveis: 4 + Math.floor(rand() * 30),
    });
  }
  return out.sort((a, b) => b.criada.localeCompare(a.criada));
}
const ANTECIPACOES = genAntecipacoes();

// ---------- Chargebacks ----------
function genChargebacks(n = 16) {
  const out = [];
  for (let i = 0; i < n; i++) {
    const u = UNIDADES[Math.floor(rand() * UNIDADES.length)];
    const status = pick(['aberto','aberto','em_disputa','vencido','perdido','ganho']);
    out.push({
      id: 'cb_' + (5000 + i),
      txId: TRANSACOES[Math.floor(rand() * 50)].id,
      unidade: u.id, unidadeNome: u.nome,
      motivo: pick(['Fraude (4837)','Produto não recebido','Cancelamento (4855)','Não reconhecida','Crédito duplicado']),
      valor: +(80 + rand() * 700).toFixed(2),
      abertura: daysAgo(Math.floor(rand() * 30)).toISOString(),
      prazoResposta: daysAgo(-(5 + Math.floor(rand() * 15))).toISOString(),
      status,
      adquirente: pick(['cielo','rede','getnet']),
    });
  }
  return out;
}
const CHARGEBACKS = genChargebacks();

// ---------- Eventos webhook ----------
const EVENTOS_WEBHOOK = [
  'transaction.created',
  'transaction.approved',
  'transaction.captured',
  'transaction.failed',
  'transaction.refunded',
  'chargeback.opened',
  'chargeback.responded',
  'payout.scheduled',
  'payout.completed',
  'subscription.created',
  'subscription.charged',
  'subscription.canceled',
  'recurrence.retry',
  'merchant.activated',
  'antecipacao.requested',
  'antecipacao.approved',
];

// ---------- KPIs derived ----------
const KPIs = (() => {
  const aprov = TRANSACOES.filter(t => t.status === 'aprovada');
  const volume = aprov.reduce((s, t) => s + t.valor, 0);
  const aprovPct = (aprov.length / TRANSACOES.length) * 100;
  const cbPct = (TRANSACOES.filter(t => t.status === 'chargeback').length / TRANSACOES.length) * 100;
  const antecPend = ANTECIPACOES.filter(a => a.status === 'solicitada' || a.status === 'em_analise').reduce((s, a) => s + a.valor, 0);
  return { volume, qtd: TRANSACOES.length, aprovPct, cbPct, antecPend };
})();

// Daily volume 30d
const VOLUME_DIARIO = (() => {
  const map = {};
  for (let i = 29; i >= 0; i--) {
    const d = daysAgo(i);
    const k = pad(d.getDate()) + '/' + pad(d.getMonth() + 1);
    map[k] = { dia: k, total: 0, cielo: 0, rede: 0, getnet: 0, qtd: 0 };
  }
  TRANSACOES.filter(t => t.status === 'aprovada').forEach(t => {
    const d = new Date(t.data);
    const k = pad(d.getDate()) + '/' + pad(d.getMonth() + 1);
    if (map[k]) {
      map[k].total += t.valor;
      map[k][t.adquirente] = (map[k][t.adquirente] || 0) + t.valor;
      map[k].qtd += 1;
    }
  });
  return Object.values(map);
})();

// Heatmap 24x7
const HEATMAP = (() => {
  const grid = range(7).map(() => range(24).fill(0));
  TRANSACOES.forEach(t => {
    const d = new Date(t.data);
    grid[d.getDay()][d.getHours()] += 1;
  });
  return grid;
})();

const VOLUME_POR_BANDEIRA = BANDEIRAS.map(b => {
  const total = TRANSACOES.filter(t => t.bandeira === b.id && t.status === 'aprovada').reduce((s, t) => s + t.valor, 0);
  return { id: b.id, nome: b.nome, valor: total, cor: b.cor };
}).filter(x => x.valor > 0).sort((a, b) => b.valor - a.valor);

const VOLUME_POR_ADQ = ADQUIRENTES.map(a => {
  const total = TRANSACOES.filter(t => t.adquirente === a.id && t.status === 'aprovada').reduce((s, t) => s + t.valor, 0);
  return { id: a.id, nome: a.nome, valor: total, cor: a.cor };
});

// Helpers
function adqById(id){ return ADQUIRENTES.find(a => a.id === id) || ADQUIRENTES[0]; }
function bandById(id){ return BANDEIRAS.find(b => b.id === id) || { nome: id, cor: '#697386' }; }
function statusBadge(s){
  const map = {
    aprovada: { label:'Aprovada', tone:'ok' },
    pendente: { label:'Pendente', tone:'warn' },
    recusada: { label:'Recusada', tone:'err' },
    chargeback: { label:'Chargeback', tone:'err' },
    solicitada:{label:'Solicitada', tone:'info'},
    em_analise:{label:'Em análise', tone:'warn'},
    aprovado:{label:'Aprovada', tone:'ok'},
    aprovada_a:{label:'Aprovada', tone:'ok'},
    rejeitada:{label:'Rejeitada', tone:'err'},
    paga:{label:'Paga', tone:'ok'},
    aberto:{label:'Aberto', tone:'warn'},
    em_disputa:{label:'Em disputa', tone:'info'},
    vencido:{label:'Vencido', tone:'err'},
    perdido:{label:'Perdido', tone:'err'},
    ganho:{label:'Ganho', tone:'ok'},
    ativo:{label:'Ativo', tone:'ok'},
    pendente_a:{label:'Pendente', tone:'warn'},
    suspenso:{label:'Suspenso', tone:'err'},
    ok:{label:'Operacional', tone:'ok'},
    warn:{label:'Atenção', tone:'warn'},
    down:{label:'Indisponível', tone:'err'},
  };
  return map[s] || { label: s, tone: 'info' };
}

window.MOCK = {
  REDES, FILIAIS, UNIDADES, ADQUIRENTES, BANDEIRAS, FAIXAS, TAXAS_ADQUIRENTE,
  PLANOS, REGRAS_ORQUESTRADOR, MODALIDADES, TRANSACOES, ANTECIPACOES, CHARGEBACKS, EVENTOS_WEBHOOK,
  KPIs, VOLUME_DIARIO, HEATMAP, VOLUME_POR_BANDEIRA, VOLUME_POR_ADQ,
  fmtBRL, fmtNum, fmtPct, fmtDate, fmtDateTime, adqById, bandById, statusBadge, pad, range, daysAgo
};
