/**
 * Exercises the openInWord de-duplication on the REAL compiled service class,
 * with only the expensive tail (convert + launch) stubbed — the guard itself,
 * the session map and the flags are the shipped code.
 */
import fs from "node:fs";
import path from "node:path";
import os from "node:os";
import assert from "node:assert";

const { MailxService } = await import("file:///Y:/dev/email/mailx/app/packages/mailx-service/index.js");

const svc = Object.create(MailxService.prototype);
svc.wordEdits = new Map();
svc.wordEditOpens = new Map();

const dir = fs.mkdtempSync(path.join(os.tmpdir(), "wordguard-"));
let uncachedCalls = 0;
let relaunches = 0;
let resolveGate = null;

// stub the expensive tail: pretend to convert + launch, register the session
svc.openInWordUncached = async (editId, html) => {
    uncachedCalls++;
    if (resolveGate) await resolveGate;                       // hold the "conversion" open
    const filePath = path.join(dir, `${editId}-${uncachedCalls}.docx`);
    fs.writeFileSync(filePath, "docx");
    svc.wordEdits.get(editId)?.stop?.();
    svc.wordEdits.set(editId, { path: filePath, stop: () => {}, opener: "word", html });
    return { ok: true, path: filePath, opener: "word" };
};
svc.relaunchExternal = async () => { relaunches++; };

const ID = "compose-1";
const BODY = "<p>hello</p>";

// 1. Rapid repeat clicks during a slow open → ONE conversion, one window.
let release;
resolveGate = new Promise(r => { release = r; });
const clicks = [svc.openInWord(ID, BODY), svc.openInWord(ID, BODY), svc.openInWord(ID, BODY)];
release();
const results = await Promise.all(clicks);
assert.strictEqual(uncachedCalls, 1, `3 rapid clicks must convert once, got ${uncachedCalls}`);
assert.strictEqual(new Set(results.map(r => r.path)).size, 1, "all clicks must get the same file");
resolveGate = null;

// 2. Click again later, body unchanged → re-activate, no new document.
const again = await svc.openInWord(ID, BODY);
assert.strictEqual(uncachedCalls, 1, "unchanged body must not convert again");
assert.strictEqual(relaunches, 1, "unchanged body must re-raise the existing window");
assert.strictEqual(again.reused, true, "caller must be told it was a re-activation");

// 3. Body changed since hand-off → fresh document, and say the old one is dead.
const changed = await svc.openInWord(ID, "<p>hello, more</p>");
assert.strictEqual(uncachedCalls, 2, "changed body must produce a fresh document");
assert.strictEqual(changed.superseded, true, "must report that the earlier window is now orphaned");
assert.notStrictEqual(changed.path, again.path, "fresh document must be a different file");

// 4. Editor closed (sidecar reported it) → next click converts afresh.
svc.wordEdits.get(ID).closed = true;
const afterClose = await svc.openInWord(ID, "<p>hello, more</p>");
assert.strictEqual(uncachedCalls, 3, "a closed document must not be re-activated");
assert.ok(!afterClose.reused, "closed session must not report reuse");
assert.ok(!afterClose.superseded, "a closed window is not superseded — it is gone");

// 5. File deleted underneath us → convert afresh, no crash.
fs.unlinkSync(svc.wordEdits.get(ID).path);
const afterUnlink = await svc.openInWord(ID, "<p>hello, more</p>");
assert.strictEqual(uncachedCalls, 4, "missing file must force a fresh conversion");
assert.ok(!afterUnlink.reused);

// 6. Two different compose windows must not interfere.
const other = await svc.openInWord("compose-2", BODY);
assert.strictEqual(uncachedCalls, 5, "a different editId is a different document");
assert.notStrictEqual(other.path, afterUnlink.path);

// 7. A failing open must not wedge the guard — the next click retries.
svc.openInWordUncached = async () => { throw new Error("html-to-docx exploded"); };
await assert.rejects(() => svc.openInWord("compose-3", BODY));
let retried = false;
svc.openInWordUncached = async (editId, html) => {
    retried = true;
    const p = path.join(dir, "retry.docx");
    fs.writeFileSync(p, "docx");
    svc.wordEdits.set(editId, { path: p, stop: () => {}, opener: "word", html });
    return { ok: true, path: p, opener: "word" };
};
await svc.openInWord("compose-3", BODY);
assert.ok(retried, "a failed open must leave the button usable, not latched");

fs.rmSync(dir, { recursive: true, force: true });
console.log("word-edit guard: 7 scenarios passed");
