const express = require("express");
const path = require("path");
const crypto = require("crypto");
const { generateDocxBuffer } = require("./docx-export");
const app = express();

app.set("trust proxy", 1);
app.use(express.json({ limit: "2mb" }));

// ─── BASE PATH (sous-dossier o2switch) ─────────────────────
const BASE = process.env.BASE_PATH || "";   // ex: "/note-osint"

// Strip le préfixe BASE des URLs entrantes (Passenger o2switch ne le fait pas)
if (BASE) {
  app.use((req, res, next) => {
    if (req.url.startsWith(BASE)) {
      req.url = req.url.slice(BASE.length) || "/";
    }
    next();
  });
}

// ─── HEADERS DE SÉCURITÉ ───────────────────────────────────
function isHttps(req) {
  return req.secure || req.headers["x-forwarded-proto"] === "https";
}

app.use((req, res, next) => {
  res.setHeader("X-Content-Type-Options", "nosniff");
  res.setHeader("X-Frame-Options", "DENY");
  res.setHeader("Referrer-Policy", "no-referrer");
  res.setHeader("Content-Security-Policy",
    "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; " +
    "style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; " +
    "frame-ancestors 'none'; base-uri 'self'; form-action 'self'");
  if (isHttps(req)) {
    res.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
  }
  next();
});

// ─── AUTHENTIFICATION ──────────────────────────────────────
const AUTH_USER = process.env.APP_USER;
const AUTH_PASS = process.env.APP_PASS;

if (!AUTH_USER || !AUTH_PASS) {
  console.error("ERREUR FATALE : les variables d'environnement APP_USER et APP_PASS doivent être définies (cPanel > Setup Node.js App). Démarrage refusé.");
  process.exit(1);
}

// Comparaison en temps constant (anti-timing attack)
function safeEqual(a, b) {
  const ha = crypto.createHash("sha256").update(String(a)).digest();
  const hb = crypto.createHash("sha256").update(String(b)).digest();
  return crypto.timingSafeEqual(ha, hb);
}

// Sessions serveur : jeton aléatoire → expiration
const SESSION_TTL_MS = 24 * 60 * 60 * 1000; // 24 h
const sessions = new Map();

function createSession() {
  const token = crypto.randomBytes(32).toString("hex");
  sessions.set(token, Date.now() + SESSION_TTL_MS);
  return token;
}

function isValidSession(token) {
  if (!token || !sessions.has(token)) return false;
  if (Date.now() > sessions.get(token)) { sessions.delete(token); return false; }
  return true;
}

// Purge périodique des sessions expirées
setInterval(() => {
  const now = Date.now();
  for (const [t, exp] of sessions) if (now > exp) sessions.delete(t);
}, 60 * 60 * 1000).unref();

function getToken(req) {
  const m = (req.headers.cookie || "").match(/auth=([^;]+)/);
  return m ? m[1] : null;
}

function sessionCookie(req, token, maxAgeSec) {
  return "auth=" + token +
    "; Path=" + (BASE || "/") +
    "; Max-Age=" + maxAgeSec +
    "; HttpOnly; SameSite=Lax" +
    (isHttps(req) ? "; Secure" : "");
}

function requireAuth(req, res, next) {
  if (req.path === "/login" || req.path === "/logout") return next();
  if (isValidSession(getToken(req))) return next();
  if (!req.path.startsWith("/api")) return res.redirect(BASE + "/login");
  res.status(401).json({ error: { message: "Non autorisé — reconnectez-vous." } });
}

app.use(requireAuth);

// ─── RATE LIMITING (en mémoire, sans dépendance) ───────────
function makeLimiter(maxHits, windowMs) {
  const hits = new Map();
  setInterval(() => {
    const now = Date.now();
    for (const [k, v] of hits) if (now > v.resetAt) hits.delete(k);
  }, windowMs).unref();
  return function (key) {
    const now = Date.now();
    let e = hits.get(key);
    if (!e || now > e.resetAt) { e = { count: 0, resetAt: now + windowMs }; hits.set(key, e); }
    e.count++;
    return e.count <= maxHits;
  };
}

const loginLimiter = makeLimiter(5, 15 * 60 * 1000);   // 5 tentatives / 15 min / IP
const apiLimiter = makeLimiter(30, 60 * 60 * 1000);    // 30 appels IA / heure / IP

// ─── PAGE DE LOGIN (thème blanc) ─────────────────────────
app.get("/login", (req, res) => {
  res.send(`<!DOCTYPE html><html><head>
<meta charset="utf-8"><title>Connexion — Analyste Sécuritaire</title>
<style>
  *{box-sizing:border-box;margin:0;padding:0}
  body{background:#f8fafc;color:#1e293b;font-family:'Segoe UI',Arial,sans-serif;min-height:100vh;display:flex;align-items:center;justify-content:center}
  .card{background:white;border:1px solid #cbd5e1;border-radius:16px;padding:40px;width:100%;max-width:400px;box-shadow:0 4px 16px rgba(0,0,0,.06)}
  .logo{display:flex;align-items:center;gap:12px;margin-bottom:32px;justify-content:center}
  .logo-icon{width:48px;height:48px;background:#1a3a5c;border-radius:12px;display:flex;align-items:center;justify-content:center;font-size:24px}
  .logo h1{font-size:18px;font-weight:700;color:#1a3a5c}
  .logo p{font-size:12px;color:#64748b}
  label{display:block;font-size:11px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px}
  input{width:100%;background:white;border:1px solid #cbd5e1;border-radius:8px;padding:12px 16px;color:#1e293b;font-size:14px;outline:none;margin-bottom:16px}
  input:focus{border-color:#2563eb;box-shadow:0 0 0 3px rgba(37,99,235,.1)}
  button{width:100%;background:#2563eb;color:white;border:none;border-radius:8px;padding:13px;font-size:15px;font-weight:600;cursor:pointer;margin-top:8px}
  button:hover{background:#1d4ed8}
  .error{background:rgba(220,38,38,.06);border:1px solid #dc2626;border-radius:8px;padding:12px;color:#dc2626;font-size:13px;margin-bottom:16px;display:none}
</style></head><body>
<div class="card">
  <div class="logo">
    <div class="logo-icon">🛡️</div>
    <div><h1>Analyste Sécuritaire</h1><p>Accès sécurisé requis</p></div>
  </div>
  <div class="error" id="err">Identifiants incorrects. Réessayez.</div>
  <label>Identifiant</label>
  <input type="text" id="u" placeholder="Votre identifiant" autocomplete="username">
  <label>Mot de passe</label>
  <input type="password" id="p" placeholder="Votre mot de passe" autocomplete="current-password">
  <button onclick="login()">Se connecter</button>
</div>
<script>
var BASE = "${BASE}";
function login() {
  const u = document.getElementById("u").value;
  const p = document.getElementById("p").value;
  fetch(BASE + "/login", {
    method: "POST",
    headers: {"Content-Type":"application/json"},
    body: JSON.stringify({u, p})
  }).then(r => r.json()).then(d => {
    if (d.ok) {
      window.location.href = BASE + "/";
    } else {
      const err = document.getElementById("err");
      err.textContent = d.error || "Identifiants incorrects. Réessayez.";
      err.style.display = "block";
    }
  });
}
document.addEventListener("keydown", e => { if (e.key === "Enter") login(); });
</script>
</body></html>`);
});

// ─── TRAITEMENT LOGIN ──────────────────────────────────────
app.post("/login", (req, res) => {
  const ip = req.ip || "?";
  if (!loginLimiter(ip)) {
    return res.status(429).json({ ok: false, error: "Trop de tentatives. Réessayez dans 15 minutes." });
  }
  const { u, p } = req.body || {};
  if (typeof u === "string" && typeof p === "string" && safeEqual(u, AUTH_USER) && safeEqual(p, AUTH_PASS)) {
    const token = createSession();
    res.setHeader("Set-Cookie", sessionCookie(req, token, SESSION_TTL_MS / 1000));
    res.json({ ok: true });
  } else {
    res.json({ ok: false });
  }
});

// ─── LOGOUT ────────────────────────────────────────────────
app.get("/logout", (req, res) => {
  const token = getToken(req);
  if (token) sessions.delete(token);
  res.setHeader("Set-Cookie", sessionCookie(req, "", 0));
  res.redirect(BASE + "/login");
});

// ─── FICHIERS STATIQUES ────────────────────────────────────
app.use(express.static(path.join(__dirname, "public")));

// ─── PROXY API ANTHROPIC (durci) ───────────────────────────
const ALLOWED_MODELS = new Set([
  "claude-haiku-4-5-20251001",
  "claude-sonnet-5",
  "claude-opus-4-8",
  "claude-fable-5",
  // anciens modèles conservés pour compatibilité (onglets ouverts avant mise à jour)
  "claude-sonnet-4-6",
  "claude-opus-4-6",
]);
const MAX_TOKENS_CAP = 16000;
const CLAUDE_TIMEOUT_MS = 280 * 1000; // sous la limite Passenger (~5 min)

app.post("/api/claude", async (req, res) => {
  const apiKey = process.env.ANTHROPIC_API_KEY;
  if (!apiKey) return res.status(500).json({ error: { message: "Configuration serveur incomplète." } });

  if (!apiLimiter(req.ip || "?")) {
    return res.status(429).json({ error: { message: "Quota horaire de génération atteint. Réessayez plus tard." } });
  }

  const { model, max_tokens, messages, system, tools } = req.body || {};

  if (!ALLOWED_MODELS.has(model)) {
    return res.status(400).json({ error: { message: "Modèle non autorisé." } });
  }
  if (!Array.isArray(messages) || messages.length === 0 || messages.length > 10) {
    return res.status(400).json({ error: { message: "Requête invalide." } });
  }

  const body = {
    model,
    max_tokens: Math.min(Math.max(parseInt(max_tokens, 10) || 8000, 1), MAX_TOKENS_CAP),
    messages,
  };
  if (typeof system === "string" && system.length <= 50000) {
    // cache_control : le prompt système est facturé ~10 % dès le 2e appel dans les 5 min
    body.system = [{ type: "text", text: system, cache_control: { type: "ephemeral" } }];
  }
  if (Array.isArray(tools)) {
    const search = tools.find(t => t && t.type === "web_search_20250305");
    if (search) body.tools = [{ type: "web_search_20250305", name: "web_search" }];
  }

  try {
    const response = await fetch("https://api.anthropic.com/v1/messages", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "x-api-key": apiKey,
        "anthropic-version": "2023-06-01",
      },
      body: JSON.stringify(body),
      signal: AbortSignal.timeout(CLAUDE_TIMEOUT_MS),
    });

    const data = await response.json();
    if (!response.ok) return res.status(response.status).json(data);
    res.json(data);
  } catch (err) {
    console.error("Erreur proxy Claude:", err);
    if (err.name === "TimeoutError" || err.name === "AbortError") {
      return res.status(504).json({ error: { message: "Délai dépassé — la génération a pris trop de temps. Réessayez, éventuellement avec un modèle plus rapide." } });
    }
    res.status(502).json({ error: { message: "Erreur serveur lors de l'appel au modèle. Réessayez." } });
  }
});

// ─── EXPORT DOCX ───────────────────────────────────────────
app.post("/api/export-docx", async (req, res) => {
  try {
    const { markdown, formData } = req.body;
    if (!markdown || !formData) {
      return res.status(400).json({ error: "Données manquantes pour l'export." });
    }

    const buffer = await generateDocxBuffer(markdown, formData);
    const filename = "Note_Securitaire_" + (formData.zone || "Zone").replace(/[^\p{L}\p{N} _-]/gu, "").replace(/\s+/g, "_") + ".docx";

    res.setHeader("Content-Type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
    res.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
    res.send(Buffer.from(buffer));
  } catch (err) {
    console.error("Erreur export DOCX:", err);
    res.status(500).json({ error: "Erreur lors de la génération du fichier DOCX." });
  }
});

// ─── TOUTES LES ROUTES → index.html ───────────────────────
app.get("*", (_, res) => res.sendFile(path.join(__dirname, "public", "index.html")));

// ─── DÉMARRAGE ─────────────────────────────────────────────
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Serveur démarré sur le port ${PORT}`));
