'use strict';
// Licentie-laag voor ModelRailPro.
//
// TWEE KANTEN:
//  1) OFFLINE — licentiesleutels worden door de uitgever (jij) ONDERTEKEND met een
//     privésleutel (Ed25519) en offline geverifieerd met de ingebouwde PUBLIEKE sleutel.
//     Werkt zonder internet; een proef/jaarlicentie draagt z'n eigen vervaldatum.
//  2) ONLINE — is er internet, dan vraagt de app de server om de ACTUELE sleutel voor
//     dit account. Zo werkt verlengen/wijzigen of intrekken automatisch door, zónder dat
//     de gebruiker opnieuw hoeft te plakken. Geen internet? Dan blijft de offline sleutel
//     gelden tot z'n vervaldatum (daarna handmatig een nieuwe sleutel plakken).
//
// Afdwingen staat standaard AAN (besturen geblokkeerd zonder geldige licentie),
// uit te zetten met BB_ENFORCE_LICENSE=0.
const crypto = require('crypto');
const https = require('https');
const fs = require('fs');
const path = require('path');

// Publieke verificatiesleutel (de privésleutel houdt de uitgever offline).
const PUBKEY_B64 = 'ftcTyaT2N54myPqzljj8EN49SPIimTQKyy+TzXWMTUk=';
const ENFORCE = process.env.BB_ENFORCE_LICENSE !== '0';
const LIC_FILE = path.join(require('./paths').DATA, 'license.json');
// Online-controle bij de uitgever. Override met BB_LICENSE_URL.
const CHECK_URL = process.env.BB_LICENSE_URL || 'https://modelrailpro.com/api-license.php';

function pubKey() {
  const raw = Buffer.from(PUBKEY_B64, 'base64');
  const der = Buffer.concat([Buffer.from('302a300506032b6570032100', 'hex'), raw]);
  return crypto.createPublicKey({ key: der, format: 'der', type: 'spki' });
}

// Verifieer een licentiesleutel ("payload.signature", beide base64url).
function verifyKey(key) {
  try {
    const [p, s] = String(key).trim().split('.');
    if (!p || !s) return { valid: false, reason: 'ongeldig formaat' };
    const payloadBuf = Buffer.from(p, 'base64url');
    const sig = Buffer.from(s, 'base64url');
    if (!crypto.verify(null, payloadBuf, pubKey(), sig)) return { valid: false, reason: 'handtekening klopt niet' };
    const payload = JSON.parse(payloadBuf.toString('utf8'));
    if (payload.exp && Date.parse(payload.exp) < Date.now()) {
      return { valid: false, reason: 'verlopen op ' + payload.exp, payload };
    }
    return { valid: true, payload };
  } catch (e) {
    return { valid: false, reason: 'kon niet lezen' };
  }
}

// Handtekening klopt? (los van vervaldatum) — om te bepalen of een sleutel ECHT van ons is.
function signatureOk(key) {
  try {
    const [p, s] = String(key).trim().split('.');
    if (!p || !s) return false;
    return crypto.verify(null, Buffer.from(p, 'base64url'), pubKey(), Buffer.from(s, 'base64url'));
  } catch (_) { return false; }
}
function expOf(key) {
  try { return JSON.parse(Buffer.from(String(key).split('.')[0], 'base64url').toString('utf8')).exp || null; }
  catch (_) { return null; }
}

function readStored() {
  try { return JSON.parse(fs.readFileSync(LIC_FILE, 'utf8')); } catch (_) { return null; }
}
function writeStored(key) {
  try { fs.writeFileSync(LIC_FILE, JSON.stringify({ key: String(key).trim() }, null, 2)); return true; }
  catch (_) { return false; }
}

// Huidige status — dit lezen de server + UI (altijd offline, betrouwbaar).
function status() {
  const stored = readStored();
  let lic = { valid: false, reason: 'geen licentie' };
  if (stored && stored.key) lic = verifyKey(stored.key);
  const out = {
    enforced: ENFORCE,
    valid: lic.valid,
    tier: lic.valid ? 'pro' : 'free',
    name: lic.payload ? lic.payload.n : null,
    type: lic.payload ? lic.payload.t : null,          // 'trial' | 'yearly' | 'lifetime'
    expires: lic.payload ? (lic.payload.exp || null) : null,
    reason: lic.reason || null,
    locked: ENFORCE && !lic.valid,
  };
  out.message = !ENFORCE
    ? 'Gratis versie — open voor iedereen.'
    : (lic.valid
        ? ('Licentie actief' + (out.name ? ' · ' + out.name : '') + (out.expires ? ' · geldig t/m ' + out.expires.slice(0, 10) : ' · onbeperkt'))
        : 'Licentie vereist — ' + (lic.reason || 'geen geldige licentie'));
  return out;
}

// Sla een (geldige) sleutel op.
function activate(key) {
  const r = verifyKey(key);
  if (!r.valid) return { ok: false, error: r.reason };
  if (!writeStored(key)) return { ok: false, error: 'kon niet opslaan' };
  return { ok: true, status: status() };
}

// ---- ONLINE: vraag de uitgever om de actuele sleutel voor dit account ----------
function postJSON(url, data, timeoutMs = 6000) {
  return new Promise((resolve) => {
    let u; try { u = new URL(url); } catch (_) { return resolve(null); }
    const payload = Buffer.from(JSON.stringify(data));
    const req = https.request(u, {
      method: 'POST', timeout: timeoutMs,
      headers: { 'Content-Type': 'application/json', 'Content-Length': payload.length },
    }, (res) => {
      if (res.statusCode !== 200) { res.resume(); return resolve(null); }
      let body = '';
      res.on('data', (c) => { body += c; if (body.length > 1e5) req.destroy(); });
      res.on('end', () => { try { resolve(JSON.parse(body)); } catch (_) { resolve(null); } });
    });
    req.on('timeout', () => req.destroy());
    req.on('error', () => resolve(null));
    req.end(payload);
  });
}

let _lastCheck = 0;
// force=true negeert de cooldown. Faalt stil (offline = geen probleem).
async function refresh(force) {
  const stored = readStored();
  if (!stored || !stored.key) return { changed: false, status: status() };
  if (!force && (Date.now() - _lastCheck) < 6 * 3600e3) return { changed: false, status: status() };
  _lastCheck = Date.now();
  const resp = await postJSON(CHECK_URL, { key: stored.key });
  if (!resp) return { changed: false, status: status() };          // offline / fout → ongemoeid
  // Server kan het account hebben ingetrokken/verwijderd → sleutel weghalen (vergrendelt).
  if (resp.revoked) { try { fs.unlinkSync(LIC_FILE); } catch (_) {} return { changed: true, status: status() }; }
  // Nieuwe, écht door ons ondertekende sleutel? Dan overnemen (verlenging/wijziging).
  if (resp.key && resp.key !== stored.key && signatureOk(resp.key)) {
    const oldExp = expOf(stored.key), newExp = expOf(resp.key);
    let better;
    if (newExp === null) better = true;          // lifetime → altijd overnemen
    else if (oldExp === null) better = false;    // hadden al lifetime → niet downgraden
    else better = Date.parse(newExp) >= Date.parse(oldExp);
    if (better) { writeStored(resp.key); return { changed: true, status: status() }; }
  }
  return { changed: false, status: status() };
}

function currentKey() { const s = readStored(); return (s && s.key) ? String(s.key) : ''; }

module.exports = { status, verifyKey, activate, refresh, currentKey, ENFORCE };
