const fs = require("fs");
const path = require("path");
const vm = require("vm");

const scriptPath = path.join(__dirname, "script.js");
const scriptSource = fs.readFileSync(scriptPath, "utf8");

function createClassList() {
  const values = new Set();
  return {
    toggle(name, enabled) {
      if (enabled) values.add(name);
      else values.delete(name);
    },
    has(name) {
      return values.has(name);
    },
  };
}

function createForm(valid, entries) {
  const listeners = {};
  let resetCount = 0;
  return {
    __entries: entries,
    reportValidity() {
      return valid;
    },
    reset() {
      resetCount += 1;
    },
    get resetCount() {
      return resetCount;
    },
    addEventListener(type, handler) {
      listeners[type] = handler;
    },
    dispatch(type, event) {
      if (!listeners[type]) throw new Error(`missing listener: ${type}`);
      return listeners[type](event);
    },
  };
}

function createButton() {
  const listeners = {};
  return {
    addEventListener(type, handler) {
      listeners[type] = handler;
    },
    dispatch(type, event) {
      if (!listeners[type]) throw new Error(`missing listener: ${type}`);
      return listeners[type](event);
    },
  };
}

function createEnv({ valid = true, fetchOk = true, clipboardFails = false } = {}) {
  const timers = [];
  const output = { textContent: "", classList: createClassList() };
  const heroAnimation = {
    src: "./squaremcp_launch.gif",
    dataset: {
      posterSrc: "./squaremcp_launch_poster.png",
      playMs: "9600",
    },
  };
  const entries = [
    ["name", "Casey"],
    ["email", "casey@example.com"],
    ["company", "SquareMCP Labs"],
    ["role", "Founder"],
    ["use_case", "Internal support copilot"],
    ["timeline", "Within 2 weeks"],
    ["systems", "Postgres, internal REST APIs"],
    ["requirements", "Audit logs and SSO"],
  ];
  const form = createForm(valid, entries);
  const button = createButton();
  const fetchCalls = [];
  let clipboardText = null;

  const context = {
    console,
    window: {
      setTimeout(fn, ms) {
        timers.push({ fn, ms });
        return timers.length;
      },
    },
    document: {
      getElementById(id) {
        if (id === "pilotIntakeForm") return form;
        if (id === "pilotOutput") return output;
        if (id === "copyRequestButton") return button;
        if (id === "heroAnimation") return heroAnimation;
        return null;
      },
    },
    navigator: {
      clipboard: {
        async writeText(value) {
          if (clipboardFails) throw new Error("clipboard denied");
          clipboardText = value;
        },
      },
    },
    fetch: async (url, options) => {
      fetchCalls.push({ url, options });
      if (!fetchOk) {
        return {
          ok: false,
          status: 500,
          async json() {
            return { error: "submit failed" };
          },
        };
      }
      return {
        ok: true,
        status: 201,
        async json() {
          return { ok: true, request_id: "req-123" };
        },
      };
    },
    FormData: class MockFormData {
      constructor(target) {
        this.target = target;
      }
      entries() {
        return this.target.__entries[Symbol.iterator]();
      }
      [Symbol.iterator]() {
        return this.entries();
      }
    },
  };

  vm.createContext(context);
  vm.runInContext(scriptSource, context, { filename: "script.js" });

  return {
    form,
    button,
    output,
    heroAnimation,
    timers,
    fetchCalls,
    getClipboardText: () => clipboardText,
  };
}

function assert(condition, message) {
  if (!condition) throw new Error(message);
}

async function run() {
  const validEnv = createEnv();
  assert(validEnv.timers.length === 1, "hero animation timer missing");
  assert(validEnv.timers[0].ms === 9600, "hero animation timer mismatch");
  validEnv.timers[0].fn();
  assert(validEnv.heroAnimation.src === "./squaremcp_launch_poster.png", "hero poster swap failed");

  let prevented = false;
  await validEnv.form.dispatch("submit", {
    preventDefault() {
      prevented = true;
    },
  });
  assert(prevented, "submit did not prevent default");
  assert(validEnv.fetchCalls.length === 1, "submit did not call fetch");
  assert(validEnv.fetchCalls[0].url === "/api/pilot-request", "submit endpoint mismatch");
  assert(validEnv.fetchCalls[0].options.method === "POST", "submit method mismatch");
  const payload = JSON.parse(validEnv.fetchCalls[0].options.body);
  assert(payload.company === "SquareMCP Labs", "submit payload mismatch");
  assert(validEnv.form.resetCount === 1, "form did not reset after success");
  assert(validEnv.output.textContent.includes("Saved to SquareMCP intake successfully."), "submit success message missing");
  assert(validEnv.output.classList.has("ready"), "submit did not set ready class");

  await validEnv.button.dispatch("click", {});
  assert(validEnv.getClipboardText().includes("SquareMCP Labs"), "copy handler did not write clipboard");
  assert(validEnv.output.textContent.includes("Copied to clipboard."), "copy success message missing");

  const invalidEnv = createEnv({ valid: false });
  await invalidEnv.form.dispatch("submit", { preventDefault() {} });
  assert(invalidEnv.fetchCalls.length === 0, "invalid submit should not call fetch");
  assert(invalidEnv.output.textContent === "", "invalid submit should not update output");

  const failureEnv = createEnv({ fetchOk: false });
  await failureEnv.form.dispatch("submit", { preventDefault() {} });
  assert(failureEnv.output.textContent.includes("Submission failed."), "submit failure message missing");
  assert(failureEnv.form.resetCount === 0, "failed submit should not reset form");

  const clipboardFailureEnv = createEnv({ clipboardFails: true });
  await clipboardFailureEnv.button.dispatch("click", {});
  assert(
    clipboardFailureEnv.output.textContent.includes("Clipboard access failed."),
    "clipboard failure message missing"
  );

  console.log("squaremcp product site smoke test: PASS");
}

run().catch((error) => {
  console.error(`squaremcp product site smoke test: FAIL: ${error.message}`);
  process.exit(1);
});
