const assert = require("node:assert/strict");
const { readFile } = require("node:fs/promises");
const path = require("node:path");
const test = require("node:test");

const {
  createInitialMealSnapshotBetaUiState,
  mapMealSnapshotResultToUiState
} = require("../js/meal-snapshot-ui-state.js");
const {
  renderMealSnapshotBetaIfEnabled,
  renderMealSnapshotBetaState
} = require("../js/meal-snapshot-ui-renderer.js");

class FakeElement {
  constructor(tagName, ownerDocument) {
    this.tagName = String(tagName).toUpperCase();
    this.ownerDocument = ownerDocument;
    this.children = [];
    this.attributes = {};
    this.listeners = {};
    this.className = "";
    this._textContent = "";
  }

  set textContent(value) {
    this._textContent = String(value);
    this.children = [];
  }

  get textContent() {
    return this._textContent + this.children.map((child) => child.textContent).join("");
  }

  set innerHTML(value) {
    throw new Error(`Unsafe innerHTML assignment: ${value}`);
  }

  append(...nodes) {
    this.children.push(...nodes);
  }

  replaceChildren(...nodes) {
    this._textContent = "";
    this.children = [...nodes];
  }

  setAttribute(name, value) {
    this.attributes[name] = String(value);
  }

  addEventListener(type, listener) {
    this.listeners[type] = listener;
  }

  click() {
    this.listeners.click?.({ currentTarget: this });
  }
}

class FakeDocument {
  createElement(tagName) {
    return new FakeElement(tagName, this);
  }
}

const ENABLED_FEATURE = Object.freeze({
  enabled: true,
  allowConfirmedSave: false
});

const DISABLED_FEATURE = Object.freeze({
  enabled: false,
  allowConfirmedSave: false
});

function fixture() {
  const documentRef = new FakeDocument();
  return { documentRef, container: documentRef.createElement("div") };
}

function descendants(node) {
  return node.children.flatMap((child) => [child, ...descendants(child)]);
}

function actionButtons(container) {
  return descendants(container).filter((node) => node.tagName === "BUTTON");
}

test("disabled guarded path leaves existing UI untouched", () => {
  const { container, documentRef } = fixture();
  const legacy = documentRef.createElement("div");
  legacy.textContent = "Existing mock Meal Snapshot";
  container.append(legacy);

  const mounted = renderMealSnapshotBetaIfEnabled(
    container,
    DISABLED_FEATURE,
    createInitialMealSnapshotBetaUiState(DISABLED_FEATURE)
  );

  assert.equal(mounted, false);
  assert.equal(container.children.length, 1);
  assert.equal(container.textContent, "Existing mock Meal Snapshot");
});

test("renderer uses textContent and treats markup-like state text as plain text", () => {
  const { container } = fixture();
  renderMealSnapshotBetaState(container, {
    status: "generic_error",
    title: "<img src=x onerror=alert(1)>",
    message: "<script>unsafe()</script>",
    disclaimer: "Review before saving.",
    canRetry: false,
    canManualEntry: false,
    canNoteOnly: false,
    canSaveConfirmed: false
  });
  assert.match(container.textContent, /<img src=x onerror=alert\(1\)>/);
  assert.match(container.textContent, /<script>unsafe\(\)<\/script>/);
});

test("missing, invalid, oversized, and unavailable states render safe copy", () => {
  const cases = [
    ["file_required", "Choose a meal photo"],
    ["unsupported_media_type", "Choose a different photo"],
    ["payload_too_large", "This photo is too large"],
    ["provider_unavailable", "Meal Snapshot is temporarily unavailable"]
  ];

  for (const [status, expectedTitle] of cases) {
    const { container } = fixture();
    const state = mapMealSnapshotResultToUiState(ENABLED_FEATURE, { status });
    renderMealSnapshotBetaState(container, state);
    assert.match(container.textContent, new RegExp(expectedTitle));
    assert.equal(container.textContent.includes("provider response"), false);
  }
});

test("safe actions render only when allowed and invoke supplied callbacks", () => {
  const { container } = fixture();
  const called = [];
  renderMealSnapshotBetaState(container, {
    status: "retry_available",
    title: "Try again",
    message: "Choose what to do next.",
    disclaimer: "Review before saving.",
    canRetry: true,
    canManualEntry: false,
    canNoteOnly: true,
    canSaveConfirmed: false
  }, {
    onRetry: () => called.push("retry"),
    onManualEntry: () => called.push("manual"),
    onNoteOnly: () => called.push("note")
  });

  const buttons = actionButtons(container);
  assert.deepEqual(buttons.map((button) => button.attributes["data-meal-snapshot-action"]), ["retry", "save_note_only"]);
  buttons.forEach((button) => button.click());
  assert.deepEqual(called, ["retry", "note"]);
});

test("confirmed save is never rendered even from an unsafe state object", () => {
  const { container } = fixture();
  renderMealSnapshotBetaState(container, {
    status: "draft_estimate",
    title: "Estimated Meal Snapshot",
    message: "Review required.",
    disclaimer: "Review before saving.",
    canRetry: false,
    canManualEntry: false,
    canNoteOnly: false,
    canSaveConfirmed: true,
    requiresReview: true
  }, {
    onSaveConfirmed: () => { throw new Error("Save must not be reachable"); }
  });

  assert.equal(actionButtons(container).some((button) => /save|confirm/i.test(button.textContent)), false);
});

test("draft estimate renders approved labels and the required disclaimer", () => {
  const { container } = fixture();
  const state = mapMealSnapshotResultToUiState(ENABLED_FEATURE, {
    status: "review_needed",
    estimate: { title: "Raw private title", calories: 650 }
  });
  renderMealSnapshotBetaState(container, state);

  assert.match(container.textContent, /Estimated Meal Snapshot/);
  assert.match(container.textContent, /Estimate - review required/);
  assert.match(container.textContent, /This is an estimate based on your photo/);
  assert.equal(container.textContent.includes("Raw private title"), false);
  assert.equal(container.textContent.includes("650"), false);
});

test("filenames and unknown provider-like fields are ignored", () => {
  const { container } = fixture();
  renderMealSnapshotBetaState(container, {
    status: "generic_error",
    title: "Safe title",
    message: "Safe message",
    disclaimer: "Review before saving.",
    canRetry: false,
    canManualEntry: false,
    canNoteOnly: false,
    filename: "patient-jane-private-meal.jpg",
    rawProviderResponse: "secret provider body",
    estimate: { title: "Private meal", calories: 1234 }
  });
  const rendered = container.textContent;
  assert.equal(rendered.includes("patient-jane"), false);
  assert.equal(rendered.includes("secret provider"), false);
  assert.equal(rendered.includes("Private meal"), false);
  assert.equal(rendered.includes("1234"), false);
});

test("rerendering clears stale state text and actions", () => {
  const { container } = fixture();
  renderMealSnapshotBetaState(container, {
    status: "retry_available",
    title: "Old retry state",
    message: "Old message",
    disclaimer: "Old disclaimer",
    canRetry: true,
    canManualEntry: true,
    canNoteOnly: true
  });
  renderMealSnapshotBetaState(container, {
    status: "idle",
    title: "New idle state",
    message: "New message",
    disclaimer: "New disclaimer",
    canRetry: false,
    canManualEntry: false,
    canNoteOnly: false
  });

  assert.match(container.textContent, /New idle state/);
  assert.equal(container.textContent.includes("Old retry state"), false);
  assert.equal(actionButtons(container).length, 0);
});

test("Phase 1C integration remains guarded and adds no unsafe runtime paths", async () => {
  const rendererSource = await readFile(path.join(__dirname, "../js/meal-snapshot-ui-renderer.js"), "utf8");
  const renderSource = await readFile(path.join(__dirname, "../js/render.js"), "utf8");
  const html = await readFile(path.join(__dirname, "../leaner-dashboard.html"), "utf8");
  const config = await readFile(path.join(__dirname, "../js/config.js"), "utf8");

  assert.doesNotMatch(rendererSource, /innerHTML|insertAdjacentHTML|outerHTML|FileReader|readAsDataURL|arrayBuffer\s*\(|base64/i);
  assert.doesNotMatch(rendererSource, /fetch\s*\(|XMLHttpRequest|localStorage|sessionStorage|indexedDB/i);
  assert.doesNotMatch(rendererSource, /firebase|firestore|gemini|openai|usda|authorization|bearer/i);
  assert.doesNotMatch(rendererSource, /saveSnapshot|persistSnapshot|writeSnapshot/i);
  assert.match(renderSource, /betaConfig\?\.enabled\s*===\s*true/);
  assert.match(html, /js\/meal-snapshot-ui-state\.js/);
  assert.match(html, /js\/meal-snapshot-ui-renderer\.js/);
  assert.equal(html.includes("js/meal-snapshot-service.js"), true);
  assert.doesNotMatch(renderSource, /createMealSnapshotClient\s*\(/);
  assert.match(config, /mealSnapshotBeta\s*:\s*\{[\s\S]*?enabled\s*:\s*false/);
  assert.match(config, /allowConfirmedSave\s*:\s*false/);
});
