'use strict';

/**
 * Point d'entrée du serveur — pensé pour un DOUBLE-CLIC, zéro configuration.
 *
 * Lancé par `Demarrer-serveur.bat`, il :
 *   1. génère et conserve un secret de session au premier lancement (plus de
 *      AUTH_SECRET à fabriquer à la main) ;
 *   2. choisit une base SQLite par défaut, dans `donnees/` ;
 *   3. applique les migrations et installe le référentiel ;
 *   4. active le mode WAL de SQLite (accès concurrents) ;
 *   5. démarre le serveur, puis OUVRE LE NAVIGATEUR sur une page d'accueil qui
 *      indique l'adresse à partager avec les autres postes du réseau.
 *
 * Toute variable d'environnement fournie (AUTH_SECRET, DATABASE_URL, PORT…) est
 * respectée : l'informaticien qui veut un proxy, PostgreSQL ou un port précis
 * garde la main. Mais SANS rien configurer, ça marche.
 */

const path = require('path');
const fs = require('fs');
const crypto = require('crypto');
const os = require('os');
const { spawn } = require('child_process');
const { appliquerMigrations } = require('./migrations-runtime');

const RACINE = __dirname;
const DOSSIER_DONNEES = path.join(RACINE, 'donnees');

function log(m) {
  console.log(m);
}

// ── Secret de session : généré une fois, conservé ────────────────
// Sans AUTH_SECRET, next-auth ne peut pas signer les sessions. Plutôt que
// d'exiger que l'utilisateur en fabrique un, on le génère au premier lancement
// et on le garde dans `donnees/secret.key` — stable d'un démarrage à l'autre
// (le régénérer déconnecterait tout le monde).
function obtenirSecret() {
  if (process.env.AUTH_SECRET && process.env.AUTH_SECRET.length >= 16) {
    return process.env.AUTH_SECRET;
  }
  fs.mkdirSync(DOSSIER_DONNEES, { recursive: true });
  const fichier = path.join(DOSSIER_DONNEES, 'secret.key');
  try {
    const existant = fs.readFileSync(fichier, 'utf8').trim();
    if (existant.length >= 16) return existant;
  } catch {
    /* première exécution : on le crée ci-dessous */
  }
  const secret = crypto.randomBytes(48).toString('base64');
  fs.writeFileSync(fichier, secret + '\n', { mode: 0o600 });
  log('[config] Secret de session généré (donnees/secret.key) — conservé pour les prochains démarrages.');
  return secret;
}

// ── Base de données : SQLite par défaut, chemin rendu absolu ──────
// Le client Prisma tracé résout `file:./…` par rapport à son schéma (absent du
// paquet), pas au dossier courant : on passe donc TOUJOURS un chemin absolu.
function obtenirDatabaseUrl() {
  const brut = process.env.DATABASE_URL || `file:./donnees/proximite.db`;
  if (!brut.startsWith('file:')) return brut; // PostgreSQL : intact
  const chemin = brut.slice('file:'.length);
  if (path.isAbsolute(chemin) || /^[A-Za-z]:[\\/]/.test(chemin)) return brut;
  return 'file:' + path.resolve(RACINE, chemin);
}

const AUTH_SECRET = obtenirSecret();
const DATABASE_URL = obtenirDatabaseUrl();
const PORT = process.env.PORT || '3000';

// Injection dans l'environnement : le serveur Next lit ces variables au boot.
process.env.AUTH_SECRET = AUTH_SECRET;
process.env.DATABASE_URL = DATABASE_URL;
process.env.PORT = PORT;
process.env.HOSTNAME = process.env.HOSTNAME || '0.0.0.0'; // accessible depuis le réseau local
process.env.NODE_ENV = 'production';

const prismaClientPath = path.join(RACINE, 'node_modules', '@prisma', 'client');
const migrationsDir = path.join(RACINE, 'prisma', 'migrations');

/** Adresses IPv4 locales (hors loopback) — pour partager l'accès sur le réseau. */
function adressesReseau() {
  const out = [];
  for (const cartes of Object.values(os.networkInterfaces())) {
    for (const c of cartes || []) {
      if (c.family === 'IPv4' && !c.internal) out.push(c.address);
    }
  }
  return out;
}

/** Écrit la page d'accueil (URLs réelles) et renvoie son chemin. */
function ecrireAccueil() {
  const modele = fs.readFileSync(path.join(RACINE, 'accueil.modele.html'), 'utf8');
  const reseau = adressesReseau();
  const lienLocal = `http://localhost:${PORT}`;
  const liensReseau = reseau.length
    ? reseau.map((ip) => `<li><a href="http://${ip}:${PORT}">http://${ip}:${PORT}</a></li>`).join('')
    : '<li class="muted">Aucune adresse réseau détectée — accès depuis ce poste uniquement.</li>';

  const html = modele
    .replace(/{{LIEN_LOCAL}}/g, lienLocal)
    .replace(/{{LIENS_RESEAU}}/g, liensReseau)
    .replace(/{{PORT}}/g, PORT);

  const cible = path.join(RACINE, 'accueil.html');
  fs.writeFileSync(cible, html, 'utf8');
  return cible;
}

/** Ouvre un fichier/URL dans le navigateur par défaut (Windows). */
function ouvrir(cible) {
  try {
    // `start` via cmd : gère les chemins avec espaces (le premier "" est le titre).
    spawn('cmd', ['/c', 'start', '', cible], { detached: true, stdio: 'ignore' }).unref();
  } catch {
    /* pas de navigateur (serveur sans interface) : l'URL est affichée dans la console */
  }
}

/** Attend que le serveur réponde, puis ouvre la page d'accueil. */
function ouvrirQuandPret() {
  const debut = Date.now();
  const timer = setInterval(async () => {
    try {
      const r = await fetch(`http://127.0.0.1:${PORT}/login`);
      if (!r.ok) return;
      clearInterval(timer);

      const reseau = adressesReseau();
      log('\n────────────────────────────────────────────────────────');
      log('  ✔ CYCEO Proximité Datacenter — serveur démarré');
      log(`  · Sur ce poste  : http://localhost:${PORT}`);
      for (const ip of reseau) log(`  · Sur le réseau : http://${ip}:${PORT}   (à partager)`);
      log('  · Laissez cette fenêtre OUVERTE tant que le serveur doit tourner.');
      log('────────────────────────────────────────────────────────\n');

      ouvrir(ecrireAccueil());
    } catch {
      // pas encore prêt — on réessaie, sauf si ça traîne vraiment trop.
      if (Date.now() - debut > 60000) clearInterval(timer);
    }
  }, 700);
}

(async () => {
  log('→ Préparation de la base…');
  await appliquerMigrations({ prismaClientPath, migrationsDir, databaseUrl: DATABASE_URL });

  // WAL : un serveur a des accès concurrents que le journal SQLite par défaut
  // gère mal. `$queryRawUnsafe` (pas `$executeRaw*`) car un PRAGMA renvoie une
  // ligne. Sans effet sur PostgreSQL.
  if (DATABASE_URL.startsWith('file:')) {
    try {
      const { PrismaClient } = require(prismaClientPath);
      const prisma = new PrismaClient({ datasources: { db: { url: DATABASE_URL } }, log: ['error'] });
      await prisma.$queryRawUnsafe('PRAGMA journal_mode=WAL;');
      await prisma.$queryRawUnsafe('PRAGMA busy_timeout=5000;');
      await prisma.$disconnect();
    } catch (e) {
      log(`[base] WAL non activé (${e && e.message ? e.message : e}) — démarrage poursuivi.`);
    }
  }

  log('→ Démarrage du serveur…');
  ouvrirQuandPret();
  require('./server.js');
})().catch((e) => {
  console.error(`\n✗ Démarrage impossible — ${e && e.message ? e.message : e}\n`);
  process.exit(1);
});
