/* backfill-wireless-equip.js — crée les Service Equipment OPS manquants pour les CPE SANS-FIL de F
 * (airos_ac / airosm / cambium). Résout customer+service_location via device→service→delivery_id→
 * Service Location(legacy_delivery_id). NON DESTRUCTIF (crée seulement), dédup par MAC/IP, manifeste d'annulation.
 * Usage: node backfill-wireless-equip.js            (DRY-RUN — compte + échantillon, 0 écriture)
 *        node backfill-wireless-equip.js --apply     (crée + écrit le manifeste)
 */
const erp = require('/app/lib/erp')
const mysql = require('mysql2/promise')
const fs = require('fs')
const APPLY = process.argv.includes('--apply')
const MANIFEST = '/app/data/backfill_wireless_equip.json'
const normMac = m => String(m || '').replace(/[^0-9a-fA-F]/g, '').toUpperCase()
const brandOf = (cat, model) => /cambium/i.test(cat) ? 'Cambium' : (/ubiqu|nano|rocket|litebeam|powerbeam|bullet|airmax|airos|loco/i.test((model || '') + cat) ? 'Ubiquiti' : '')

;(async () => {
  let cfg = {}; try { cfg = require('/app/lib/config') } catch (e) { /* fallback to env below */ }
  const pool = mysql.createPool({
    host: cfg.LEGACY_DB_HOST || process.env.LEGACY_DB_HOST || '10.100.80.100',
    user: cfg.LEGACY_DB_USER || process.env.LEGACY_DB_USER || 'facturation',
    password: cfg.LEGACY_DB_PASS || process.env.LEGACY_DB_PASS,
    database: cfg.LEGACY_DB_NAME || process.env.LEGACY_DB_NAME || 'gestionclient',
    connectionLimit: 2, connectTimeout: 8000,
  })
  // 1) CPE sans-fil F + delivery_id du service (préfère un service actif).
  const [rows] = await pool.query(
    "SELECT d.id device_id, d.category, d.manage, d.port, d.mac, d.sn, d.model, " +
    " (SELECT s.delivery_id FROM service s WHERE s.device_id=d.id AND s.delivery_id>0 ORDER BY (s.status=1) DESC, s.id DESC LIMIT 1) delivery_id " +
    " FROM device d WHERE d.category IN ('airos_ac','airosm','cambium') AND d.manage<>''")
  await pool.end()
  const wired = rows.filter(r => Number(r.delivery_id) > 0)

  // 2) Index Service Location OPS par legacy_delivery_id → {name, customer}
  const delIds = [...new Set(wired.map(r => Number(r.delivery_id)))]
  const slIndex = {}
  for (let i = 0; i < delIds.length; i += 100) {
    const batch = delIds.slice(i, i + 100)
    const sls = await erp.list('Service Location', { filters: [['legacy_delivery_id', 'in', batch]], fields: ['name', 'customer', 'legacy_delivery_id'], limit: 500 }).catch(() => [])
    for (const s of sls) if (s.customer) slIndex[Number(s.legacy_delivery_id)] = { name: s.name, customer: s.customer }
  }

  // 3) Service Equipment existants (ceux avec ip_address = univers sans-fil) → dédup par MAC + IP
  const existMac = new Set(), existIp = new Set()
  const existing = await erp.list('Service Equipment', { filters: [['ip_address', '!=', '']], fields: ['name', 'mac_address', 'ip_address'], limit: 8000 }).catch(() => [])
  for (const e of existing) { if (e.mac_address) existMac.add(normMac(e.mac_address)); if (e.ip_address) existIp.add(String(e.ip_address).trim()) }

  // 4) calcule les créations
  const toCreate = [], skips = { no_sl: 0, dup: 0, no_key: 0 }
  for (const r of wired) {
    const sl = slIndex[Number(r.delivery_id)]
    if (!sl) { skips.no_sl++; continue }
    const nm = normMac(r.mac), ip = String(r.manage).trim()
    if (!nm && !ip) { skips.no_key++; continue } // ni MAC ni IP → indédupable, on saute
    if ((nm && existMac.has(nm)) || (ip && existIp.has(ip))) { skips.dup++; continue }
    // serial_number OBLIGATOIRE. F sn souvent NULL pour les CPE sans-fil → repli sur la MAC (identifiant réel, unique),
    // sinon clé synthétique stable par device_id. mac_address garde le format d'origine (avec séparateurs).
    const serial = (r.sn && String(r.sn).trim()) ? String(r.sn).trim() : (nm || ('AIROS-' + r.device_id))
    const doc = {
      equipment_type: 'Autre', brand: brandOf(r.category, r.model), model: r.model || '',
      serial_number: serial, mac_address: r.mac || '', ip_address: r.manage, login_user: 'admin',
      customer: sl.customer, service_location: sl.name, status: 'Actif', ownership: 'Gigafibre',
    }
    toCreate.push({ device_id: r.device_id, mac: r.mac, ip: r.manage, doc })
  }
  console.log(JSON.stringify({ f_wireless: rows.length, with_delivery: wired.length, existing_wireless_se: existing.length, to_create: toCreate.length, skipped: skips }, null, 1))
  console.log('SAMPLE:', JSON.stringify(toCreate.slice(0, 4).map(x => x.doc), null, 1))
  if (!APPLY) { console.log('DRY-RUN — passe --apply pour créer.'); return }

  // 5) crée + manifeste
  const created = []
  let n = 0
  for (const c of toCreate) {
    const res = await erp.create('Service Equipment', c.doc).catch(e => ({ error: String(e.message || e) }))
    const okName = (res && typeof res.name === 'string') ? res.name : null
    if (okName) created.push({ name: okName, device_id: c.device_id, mac: c.mac, ip: c.ip })
    else created.push({ error: String((res && res.error) || 'échec').slice(0, 160), status: res && res.status, device_id: c.device_id, mac: c.mac })
    if (++n % 100 === 0) console.log('… ' + n + '/' + toCreate.length)
  }
  fs.writeFileSync(MANIFEST, JSON.stringify({ at: Date.now(), created }, null, 1))
  console.log('CREATED ' + created.filter(c => c.name).length + ' / ' + toCreate.length + ' · errors ' + created.filter(c => c.error).length + ' · manifest ' + MANIFEST)
})().catch(e => { console.log('FATAL', e.message) })
