'use strict'
const http = require('http')
const https = require('https')
const { URL } = require('url')
const cfg = require('./config')

function log (...args) { console.log(`[${new Date().toISOString().slice(11, 19)}]`, ...args) }

function json (res, status, data) {
  res.writeHead(status, { 'Content-Type': 'application/json' })
  res.end(JSON.stringify(data))
}

function parseBody (req) {
  return new Promise((resolve, reject) => {
    const chunks = []
    req.on('data', c => chunks.push(c))
    req.on('end', () => {
      const raw = Buffer.concat(chunks).toString()
      const ct = (req.headers['content-type'] || '').toLowerCase()
      if (ct.includes('application/json')) {
        try { resolve(JSON.parse(raw)) } catch { resolve({}) }
      } else if (ct.includes('urlencoded')) {
        resolve(Object.fromEntries(new URLSearchParams(raw)))
      } else {
        resolve(raw)
      }
    })
    req.on('error', reject)
  })
}

function httpRequest (baseUrl, path, { method = 'GET', body, headers = {}, timeout = 15000 } = {}) {
  return new Promise((resolve, reject) => {
    const u = new URL(path, baseUrl)
    const proto = u.protocol === 'https:' ? https : http
    const req = proto.request({
      hostname: u.hostname, port: u.port || (u.protocol === 'https:' ? 443 : 80),
      path: u.pathname + u.search, method,
      headers: { 'Content-Type': 'application/json', ...headers }, timeout,
    }, (resp) => {
      let data = ''
      resp.on('data', c => { data += c })
      resp.on('end', () => {
        try { resolve({ status: resp.statusCode, data: data ? JSON.parse(data) : null }) }
        catch { resolve({ status: resp.statusCode, data }) }
      })
    })
    req.on('error', reject)
    req.on('timeout', () => { req.destroy(); reject(new Error('Request timeout: ' + path)) })
    if (body) req.write(typeof body === 'string' ? body : JSON.stringify(body))
    req.end()
  })
}

function erpFetch (path, opts = {}) {
  const parsed = new URL(cfg.ERP_URL + path)
  return new Promise((resolve, reject) => {
    const req = http.request({
      hostname: parsed.hostname, port: parsed.port || 8000,
      path: parsed.pathname + parsed.search, method: opts.method || 'GET',
      headers: { Host: cfg.ERP_SITE, Authorization: 'token ' + cfg.ERP_TOKEN, 'Content-Type': 'application/json', ...opts.headers },
    }, (res) => {
      let body = ''
      res.on('data', c => body += c)
      res.on('end', () => {
        try { resolve({ status: res.statusCode, data: JSON.parse(body) }) }
        catch { resolve({ status: res.statusCode, data: body }) }
      })
    })
    req.on('error', reject)
    if (opts.body) req.write(typeof opts.body === 'string' ? opts.body : JSON.stringify(opts.body))
    req.end()
  })
}

function erpRequest (method, path, body) {
  return erpFetch(path, { method, ...(body && { body }) })
}

async function lookupCustomerByPhone (phone) {
  const digits = phone.replace(/\D/g, '').slice(-10)
  const fields = JSON.stringify(['name', 'customer_name', 'cell_phone', 'tel_home', 'tel_office'])
  for (const field of ['cell_phone', 'tel_home', 'tel_office']) {
    const filters = JSON.stringify([[field, 'like', '%' + digits]])
    const path = `/api/resource/Customer?filters=${encodeURIComponent(filters)}&fields=${encodeURIComponent(fields)}&limit_page_length=1`
    try {
      const res = await erpFetch(path)
      if (res.status === 200 && res.data?.data?.length > 0) return res.data.data[0]
    } catch (e) { log('lookupCustomerByPhone error on ' + field + ':', e.message) }
  }
  return null
}

function createCommunication (fields) {
  return erpFetch('/api/resource/Communication', { method: 'POST', body: JSON.stringify(fields) })
}

// --- GenieACS NBI rate limiter ---
// Prevents overwhelming GenieACS with concurrent requests
const NBI_MAX_CONCURRENT = parseInt(process.env.NBI_MAX_CONCURRENT || '3')
const NBI_MIN_INTERVAL_MS = parseInt(process.env.NBI_MIN_INTERVAL_MS || '200')
let nbiActive = 0
let nbiLastRequest = 0
const nbiQueue = []

function nbiRequest (path, method = 'GET', body = null) {
  return new Promise((resolve, reject) => {
    function execute () {
      nbiActive++
      const now = Date.now()
      const wait = Math.max(0, NBI_MIN_INTERVAL_MS - (now - nbiLastRequest))
      setTimeout(() => {
        nbiLastRequest = Date.now()
        httpRequest(cfg.GENIEACS_NBI_URL, path, { method, body, timeout: 30000 })
          .then(resolve).catch(reject)
          .finally(() => {
            nbiActive--
            if (nbiQueue.length > 0) nbiQueue.shift()()
          })
      }, wait)
    }
    if (nbiActive >= NBI_MAX_CONCURRENT) {
      nbiQueue.push(execute)
    } else {
      execute()
    }
  })
}

function deepGetValue (obj, path) {
  const node = path.split('.').reduce((o, k) => o?.[k], obj)
  return node?._value !== undefined ? node._value : null
}

module.exports = {
  log, json, parseBody, httpRequest,
  erpFetch, erpRequest, lookupCustomerByPhone, createCommunication,
  nbiRequest, deepGetValue,
}
