"use strict";
const crypto = require("node:crypto");
const fs = require("node:fs");
const path = require("node:path");

class MapGate {
  constructor({ mapsDir = "/etc/guanghu/navigation-maps", stateFile = "" } = {}) {
    this.mapsDir = mapsDir;
    this.stateFile = stateFile;
    this.acks = new Map();
    this.load();
  }
  read(target) {
    if (!/^[A-Z0-9-]+$/.test(target)) throw new Error("invalid_target");
    const data = JSON.parse(fs.readFileSync(path.join(this.mapsDir, `${target}.json`), "utf8"));
    const canonical = JSON.stringify(data);
    return { data, hash: crypto.createHash("sha256").update(canonical).digest("hex") };
  }
  ack(sessionToken, target, mapHash, now = Date.now() / 1000, ttl = 3600) {
    const current = this.read(target);
    if (!safeEqual(current.hash, mapHash)) return { ok: false, reason: "map_hash_mismatch" };
    this.acks.set(hash(sessionToken), { target, mapHash, expiresAt: now + ttl });
    this.persist();
    return { ok: true, expiresAt: now + ttl };
  }
  verify(sessionToken, target, mapHash, now = Date.now() / 1000) {
    const ack = this.acks.get(hash(sessionToken));
    if (!ack) return { ok: false, reason: "map_not_acknowledged" };
    if (now > ack.expiresAt) return { ok: false, reason: "map_ack_expired" };
    if (ack.target !== target) return { ok: false, reason: "map_target_mismatch" };
    if (!safeEqual(ack.mapHash, mapHash)) return { ok: false, reason: "map_changed" };
    return { ok: true };
  }
  load(now = Date.now() / 1000) {
    if (!this.stateFile || !fs.existsSync(this.stateFile)) return;
    try { for (const [key, value] of Object.entries(JSON.parse(fs.readFileSync(this.stateFile, "utf8")).acks || {})) if (value.expiresAt >= now) this.acks.set(key, value); } catch { this.acks.clear(); }
  }
  persist() {
    if (!this.stateFile) return;
    fs.writeFileSync(this.stateFile, JSON.stringify({ version: 1, acks: Object.fromEntries(this.acks) }), { mode: 0o600 });
  }
}
function hash(value) { return crypto.createHash("sha256").update(String(value)).digest("hex"); }
function safeEqual(left, right) { const a = Buffer.from(String(left)); const b = Buffer.from(String(right)); return a.length === b.length && crypto.timingSafeEqual(a, b); }
module.exports = { MapGate };
