const assert = require("assert");
const fs = require("fs");
const path = require("path");
const {
  issueConfirmation,
  consumeConfirmation,
  isDestructivePath
} = require("../src/services/destructive-confirm");

const root = path.join(__dirname, "..");
const destructivePath = /(?:^|\/)(?:delete|remove|clear|reset|renew|uninstall|cleanup|archive|revoke|unlink|unset|revert)(?:\/|$)/i;

function filesBelow(directory, extension, output = []) {
  for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
    const target = path.join(directory, entry.name);
    if (entry.isDirectory()) filesBelow(target, extension, output);
    else if (target.endsWith(extension)) output.push(target);
  }
  return output;
}

function maskEjs(source) {
  return source.replace(/<%[\s\S]*?%>/g, "EJS");
}

function attribute(tag, name) {
  return tag.match(new RegExp(`\\b${name}\\s*=\\s*["']([^"']*)["']`, "i"))?.[1] || "";
}

function requireConfirmationCopy(tag, file) {
  for (const attributeName of ["data-confirm-mode", "data-confirm-title", "data-confirm-text", "data-confirm-label"]) {
    assert(new RegExp(`\\b${attributeName}(?:\\s*=|\\s|>)`, "i").test(tag), `${path.relative(root, file)} has an unconnected destructive control missing ${attributeName}: ${tag}`);
  }
  assert.equal(attribute(tag, "data-confirm-mode"), "modal", `${path.relative(root, file)} must use the shared timed modal`);
}

function verifyViews() {
  const files = [
    ...filesBelow(path.join(root, "src", "web", "views"), ".ejs"),
    ...filesBelow(path.join(root, "plugins"), ".ejs")
  ];
  let controls = 0;
  for (const file of files) {
    const source = maskEjs(fs.readFileSync(file, "utf8"));
    assert(!/name\s*=\s*["']action["'][^>]*value\s*=\s*["']delete["']/i.test(source), `${path.relative(root, file)} hides a delete behind a generic POST route; use an explicit /delete endpoint`);
    for (const tag of source.match(/<(?:form|button)\b[^>]*>/gi) || []) {
      const action = attribute(tag, "action") || attribute(tag, "formaction");
      const confirmAction = attribute(tag, "data-confirm-action");
      if (!destructivePath.test(action) && !destructivePath.test(confirmAction)) continue;
      requireConfirmationCopy(tag, file);
      controls += 1;
    }
  }
  assert(controls >= 30, `Expected broad destructive-control coverage, found only ${controls}`);
  return controls;
}

function verifySharedInfrastructure() {
  for (const action of [
    "/admin/commands/1/delete",
    "/admin/navigation/reset",
    "/admin/updates/core/revert",
    "/plugins/auto-vc/settings/remove"
  ]) {
    assert.equal(isDestructivePath(action), true, `Server does not protect ${action}`);
  }

  const req = {
    session: { user: { id: "admin-user", isAdmin: true } },
    body: {},
    get() { return null; }
  };
  const issued = issueConfirmation(req, "/admin/updates/core/revert");
  req.session.destructive_confirmations[issued.token].not_before = Date.now() - 1;
  assert.equal(consumeConfirmation(req, "/admin/updates/core/revert", issued.token).valid, true);

  const appScript = fs.readFileSync(path.join(root, "src", "web", "public", "app.js"), "utf8");
  const updateScript = fs.readFileSync(path.join(root, "src", "web", "public", "update-management.js"), "utf8");
  assert(appScript.includes("submitter?.dataset?.confirmMode"), "Submit-button confirmation metadata is ignored");
  assert(appScript.includes("submitter?.dataset?.confirmLabel || form.dataset.confirmLabel"), "The clicked submit button cannot override generic confirmation copy");
  assert(appScript.includes('form?.getAttribute?.("action")'), "Shared form actions do not resist controls named action shadowing the endpoint");
  assert(appScript.includes("window.LumiForms = Object.freeze"), "Plugins cannot reuse the shared clobber-safe form helper");
  assert(appScript.includes('form.querySelector(\'input[name="confirmation_token"]\')'), "Timed form confirmation tokens are not attached to submitted forms");
  assert(updateScript.includes("window.LumiRequest.form(form, { submitter })"), "Async update requests do not use the shared request payload");
  assert(updateScript.includes('field.name = "confirmation_token"'), "Timed update confirmation tokens are not attached to the enhanced request");
  assert(!appScript.includes("window.LumiConfirm.destructiveFetch(form.action"), "Timed update forms request a second confirmation instead of reusing the form token");
  assert(appScript.includes('state.confirmButton.removeEventListener("click", state.onConfirm)'), "Cancelled or expired confirmations leave stale modal submissions behind");
  assert(appScript.includes("state.onConfirm = onConfirm"), "Timed form confirmations do not retain their listener for cleanup");

  const toolManagerScript = fs.readFileSync(path.join(root, "plugins", "lumi_ai", "public", "tool-manager.js"), "utf8");
  for (const field of ["confirmMode", "confirmTitle", "confirmText", "confirmLabel"]) {
    assert(toolManagerScript.includes(`form.dataset.${field}`), `Dynamic AI tool deletion is missing ${field}`);
  }

  const autoVcView = fs.readFileSync(path.join(root, "plugins", "auto-vc", "views", "auto-vc.ejs"), "utf8");
  assert(autoVcView.includes('settingsForm.action = "/plugins/auto-vc/settings/remove"'), "Auto VC lobby deletion does not switch to its protected route");

  const improvementView = fs.readFileSync(path.join(root, "plugins", "lumi_ai", "views", "improvement-center.ejs"), "utf8");
  assert(improvementView.includes("/reviews/<%= review.id %>/delete"));
  assert(improvementView.includes("/corrections/<%= entry.id %>/delete"));
  const improvementScript = fs.readFileSync(path.join(root, "plugins", "lumi_ai", "public", "improvement-center.js"), "utf8");
  assert.equal(improvementScript.includes("window.confirm"), false, "Improvement Center still uses the legacy browser confirmation");
  assert(improvementScript.includes("window.LumiForms?.action(form)"), "Improvement Center bypasses the shared clobber-safe form endpoint helper");
}

const controls = verifyViews();
verifySharedInfrastructure();
console.log(`Destructive action verification passed (${controls} controls).`);
