// Helpers compartilhados entre funções: HMAC, gen/verify de keys e tokens.
const crypto = require('crypto');

const SECRET = process.env.LICENSE_SECRET || '';
const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || '';
const BLACKLIST = (process.env.BLACKLIST || '').split(',').map(s => s.trim()).filter(Boolean);
const TOKEN_TTL_S = 24 * 3600;

function b64url(buf) {
  return buf.toString('base64').replace(/=+$/, '').replace(/\+/g, '-').replace(/\//g, '_');
}
function b64urlDecode(s) {
  s = s.replace(/-/g, '+').replace(/_/g, '/');
  while (s.length % 4) s += '=';
  return Buffer.from(s, 'base64');
}
function hmac(data, len) {
  const h = crypto.createHmac('sha256', SECRET).update(data).digest('hex');
  return len ? h.slice(0, len) : h;
}

// Key format: IRDM-{owner8}-{expires}-{sig16}
function genKey(owner, expiresTs, reuseOwnerId) {
  let ownerId;
  if (reuseOwnerId && /^[a-z0-9]{2,16}$/.test(reuseOwnerId)) {
    ownerId = reuseOwnerId;
  } else {
    const ownerClean = (owner || '').replace(/[^a-z0-9]/gi, '').slice(0, 16) || 'anon';
    const pad = b64url(crypto.randomBytes(6)).slice(0, 6);
    ownerId = (ownerClean + pad).slice(0, 8).toLowerCase();
  }
  const sig = hmac(`${ownerId}-${expiresTs}`, 16);
  return `IRDM-${ownerId}-${expiresTs}-${sig}`;
}
function parseKey(k) {
  const parts = (k || '').trim().split('-');
  if (parts.length !== 4 || parts[0] !== 'IRDM') return null;
  const ownerId = parts[1], expiresTs = parseInt(parts[2], 10), sig = parts[3];
  if (!ownerId || !expiresTs || !sig) return null;
  return { ownerId, expiresTs, sig, raw: k.trim() };
}
function verifyKey(k) {
  const p = parseKey(k);
  if (!p) return { ok: false, reason: 'formato inválido' };
  if (BLACKLIST.includes(p.raw)) return { ok: false, reason: 'licença revogada' };
  const expected = hmac(`${p.ownerId}-${p.expiresTs}`, 16);
  if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(p.sig))) {
    return { ok: false, reason: 'assinatura inválida' };
  }
  const now = Math.floor(Date.now() / 1000);
  if (now >= p.expiresTs) return { ok: false, reason: 'licença expirada', expires_at: p.expiresTs };
  return { ok: true, owner_id: p.ownerId, expires_at: p.expiresTs };
}

// Token: base64url(payload).base64url(sig) — JWT-like. payload = {key, iat, exp}
function issueToken(key) {
  const now = Math.floor(Date.now() / 1000);
  const payload = JSON.stringify({ key, iat: now, exp: now + TOKEN_TTL_S });
  const pb = b64url(Buffer.from(payload));
  const sig = b64url(crypto.createHmac('sha256', SECRET).update(pb).digest());
  return `${pb}.${sig}`;
}
function verifyToken(tok) {
  try {
    const [pb, sig] = (tok || '').split('.');
    if (!pb || !sig) return { ok: false, reason: 'token malformado' };
    const expect = b64url(crypto.createHmac('sha256', SECRET).update(pb).digest());
    if (expect !== sig) return { ok: false, reason: 'assinatura token inválida' };
    const p = JSON.parse(b64urlDecode(pb).toString('utf-8'));
    const now = Math.floor(Date.now() / 1000);
    if (now >= (p.exp || 0)) return { ok: false, reason: 'token expirado' };
    return { ok: true, ...p };
  } catch (e) {
    return { ok: false, reason: 'erro ao decodificar token' };
  }
}

function requireAdmin(req) {
  const p = req.headers['x-admin-password'] || '';
  return ADMIN_PASSWORD && p === ADMIN_PASSWORD;
}

async function readBody(req) {
  if (req.body && typeof req.body === 'object') return req.body;
  if (typeof req.body === 'string') { try { return JSON.parse(req.body); } catch { return {}; } }
  return await new Promise((resolve) => {
    let data = '';
    req.on('data', chunk => { data += chunk; });
    req.on('end', () => { try { resolve(JSON.parse(data || '{}')); } catch { resolve({}); } });
    req.on('error', () => resolve({}));
  });
}

function json(res, code, obj) {
  res.status(code).setHeader('content-type', 'application/json');
  res.setHeader('access-control-allow-origin', '*');
  res.setHeader('access-control-allow-headers', 'content-type, x-admin-password');
  res.setHeader('access-control-allow-methods', 'GET, POST, OPTIONS');
  res.end(JSON.stringify(obj));
}

function parseKeyPublic(k) {
  const p = parseKey(k);
  if (!p) return null;
  const expected = hmac(`${p.ownerId}-${p.expiresTs}`, 16);
  const sigValid = expected === p.sig;
  const now = Math.floor(Date.now() / 1000);
  return { owner_id: p.ownerId, expires_at: p.expiresTs, signature_valid: sigValid, expired: now >= p.expiresTs };
}

function listBlacklist() { return BLACKLIST; }

module.exports = { genKey, parseKeyPublic, verifyKey, issueToken, verifyToken, requireAdmin, readBody, json, listBlacklist, TOKEN_TTL_S };
