// Licences et periode d'evaluation.
//
// Une licence est un jeton signe : contenu + signature Ed25519. L'application
// n'embarque que la cle publique, qui permet de verifier une licence mais
// jamais d'en fabriquer une. La signature se fait a part, dans
// outils/emettre-licence.mjs, avec la cle privee qui ne quitte pas votre poste.
// Aucune connexion n'est requise pour verifier.

import { verify, createPublicKey } from 'node:crypto';
import { readFile, writeFile, mkdir } from 'node:fs/promises';
import path from 'node:path';
import { CLE_PUBLIQUE } from './cle-publique.mjs';

export const EVALUATION_JOURS = 15;
export const EVALUATION_CONVERSIONS = 30;

const publique = createPublicKey(CLE_PUBLIQUE);

export const versB64url = (b) => Buffer.from(b).toString('base64')
  .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');

const depuisB64url = (s) => Buffer.from(s.replace(/-/g, '+').replace(/_/g, '/'), 'base64');

/**
 * Verifie un jeton de licence.
 * Renvoie { valide, donnees, raison }.
 */
export function verifierLicence(jeton) {
  const propre = String(jeton || '')
    .replace(/-{3,}[^\n]*/g, '')   // lignes decoratives eventuelles
    .replace(/\s+/g, '')
    .trim();

  if (!propre.includes('.')) return { valide: false, raison: 'Format de licence non reconnu.' };

  const [contenu, signature] = propre.split('.');
  let donnees;
  try {
    donnees = JSON.parse(depuisB64url(contenu).toString('utf8'));
  } catch {
    return { valide: false, raison: 'Licence illisible.' };
  }

  let authentique = false;
  try {
    authentique = verify(null, Buffer.from(contenu, 'utf8'), publique, depuisB64url(signature));
  } catch {
    authentique = false;
  }
  if (!authentique) return { valide: false, raison: "Cette licence n'est pas authentique." };

  if (donnees.expire) {
    const fin = new Date(donnees.expire);
    if (Number.isFinite(fin.getTime()) && fin < new Date()) {
      return {
        valide: false,
        raison: `Licence expirée le ${fin.toLocaleDateString('fr-FR')}.`,
        donnees,
      };
    }
  }

  return { valide: true, donnees };
}

// --- Etat local : licence enregistree et compteur d'evaluation -------------

const fichiers = (dossier) => ({
  licence: path.join(dossier, 'licence.txt'),
  evaluation: path.join(dossier, 'evaluation.json'),
});

async function lireJson(chemin, defaut) {
  try {
    return JSON.parse(await readFile(chemin, 'utf8'));
  } catch {
    return defaut;
  }
}

/** Sous licence, ou en evaluation avec ce qu'il reste. */
export async function etatLicence(dossierDonnees) {
  const f = fichiers(dossierDonnees);
  let refus = null;

  try {
    const jeton = (await readFile(f.licence, 'utf8')).trim();
    const controle = verifierLicence(jeton);
    if (controle.valide) {
      return {
        sousLicence: true,
        client: controle.donnees.client ?? '',
        expire: controle.donnees.expire ?? null,
      };
    }
    // Licence presente mais refusee : on le signale sans bloquer l'evaluation.
    refus = controle.raison;
  } catch { /* aucune licence enregistree */ }

  const evaluation = await lireJson(f.evaluation, null);
  const debut = evaluation?.debut ? new Date(evaluation.debut) : new Date();
  const conversions = evaluation?.conversions ?? 0;

  const joursEcoules = Math.floor((Date.now() - debut.getTime()) / 86400000);
  const joursRestants = Math.max(0, EVALUATION_JOURS - joursEcoules);
  const conversionsRestantes = Math.max(0, EVALUATION_CONVERSIONS - conversions);

  return {
    sousLicence: false,
    refus,
    joursRestants,
    conversionsRestantes,
    epuisee: joursRestants === 0 || conversionsRestantes === 0,
  };
}

/** Demarre le decompte au premier usage, puis compte les photos converties. */
export async function compterConversion(dossierDonnees) {
  const f = fichiers(dossierDonnees);
  await mkdir(dossierDonnees, { recursive: true });
  const actuel = await lireJson(f.evaluation, null);
  await writeFile(f.evaluation, JSON.stringify({
    debut: actuel?.debut ?? new Date().toISOString(),
    conversions: (actuel?.conversions ?? 0) + 1,
  }, null, 2), 'utf8');
}

/** Enregistre une licence, apres verification. */
export async function enregistrerLicence(dossierDonnees, jeton) {
  const controle = verifierLicence(jeton);
  if (!controle.valide) return controle;
  await mkdir(dossierDonnees, { recursive: true });
  await writeFile(fichiers(dossierDonnees).licence, String(jeton).trim(), 'utf8');
  return controle;
}
