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

const MealSnapshotMockEstimate = require("../js/meal-snapshot-mock-estimate.js");
const {
  ReviewedDraftValidationError,
  createReviewedMealDraft,
  validateReviewedMealDraft
} = require("../js/meal-snapshot-reviewed-draft.js");

test("creates a strict frozen local-only reviewed draft contract", () => {
  const source = MealSnapshotMockEstimate.createMockMealSnapshotEstimate();
  const draft = createReviewedMealDraft(source, { userEdited: true });

  assert.deepEqual(Object.keys(draft), [
    "schemaVersion",
    "kind",
    "lifecycle",
    "source",
    "title",
    "nutrition",
    "ingredients",
    "review",
    "disclaimer",
    "canSave",
    "shouldSave",
    "persisted"
  ]);
  assert.equal(draft.schemaVersion, "1.0");
  assert.equal(draft.kind, "meal_snapshot_reviewed_draft");
  assert.equal(draft.lifecycle, "local_only");
  assert.equal(draft.source, "local_mock");
  assert.equal(draft.review.required, true);
  assert.equal(draft.review.userEdited, true);
  assert.equal(draft.canSave, false);
  assert.equal(draft.shouldSave, false);
  assert.equal(draft.persisted, false);
  assert.equal(validateReviewedMealDraft(draft), true);
  assert.equal(Object.isFrozen(draft), true);
  assert.equal(Object.isFrozen(draft.nutrition), true);
  assert.equal(Object.isFrozen(draft.ingredients), true);
});

test("allowlists reviewed fields and strips file, provider, auth, image, and unknown data", () => {
  const source = {
    ...MealSnapshotMockEstimate.createMockMealSnapshotEstimate(),
    originalFilename: "patient-name-breakfast.jpg",
    fileSize: 123456,
    mimeType: "image/jpeg",
    imageMetadata: { width: 1000 },
    imageData: "private-image-data",
    authToken: "secret-token",
    rawProviderResponse: { private: true },
    providerPayload: "private-provider-payload",
    hiddenField: "must-not-pass",
    leanerNote: "not part of the reviewed contract"
  };
  const draft = createReviewedMealDraft(source);
  const serialized = JSON.stringify(draft);

  for (const forbidden of [
    "patient-name",
    "123456",
    "image/jpeg",
    "width",
    "private-image-data",
    "secret-token",
    "private-provider",
    "must-not-pass",
    "not part of"
  ]) {
    assert.equal(serialized.includes(forbidden), false, forbidden);
  }
  assert.deepEqual(Object.keys(draft.ingredients[0]), ["name", "amount"]);
});

test("normalizes whitespace and bounded editable strings", () => {
  const source = MealSnapshotMockEstimate.createMockMealSnapshotEstimate();
  source.title = `  ${"T".repeat(140)}  `;
  source.ingredients[0].name = "  Croissant  ";
  source.ingredients[0].amount = "  one portion  ";
  const draft = createReviewedMealDraft(source);
  assert.equal(draft.title.length, 100);
  assert.equal(draft.ingredients[0].name, "Croissant");
  assert.equal(draft.ingredients[0].amount, "one portion");
});

test("rejects invalid reviewed values with controlled errors", () => {
  const invalidCases = [
    { mutate: (draft) => { draft.title = ""; }, code: "TITLE_REQUIRED" },
    { mutate: (draft) => { draft.calories = -1; }, code: "NUTRITION_INVALID" },
    { mutate: (draft) => { draft.protein = Number.NaN; }, code: "NUTRITION_INVALID" },
    { mutate: (draft) => { draft.ingredients = []; }, code: "INGREDIENTS_REQUIRED" },
    { mutate: (draft) => { draft.ingredients[0].amount = ""; }, code: "INGREDIENT_INVALID" }
  ];

  for (const item of invalidCases) {
    const source = MealSnapshotMockEstimate.createMockMealSnapshotEstimate();
    item.mutate(source);
    assert.throws(
      () => createReviewedMealDraft(source),
      (error) => error instanceof ReviewedDraftValidationError && error.code === item.code
    );
  }
});

test("strict validator rejects saveable, persisted, or expanded contracts", () => {
  const valid = createReviewedMealDraft(MealSnapshotMockEstimate.createMockMealSnapshotEstimate());
  assert.equal(validateReviewedMealDraft({ ...valid, canSave: true }), false);
  assert.equal(validateReviewedMealDraft({ ...valid, shouldSave: true }), false);
  assert.equal(validateReviewedMealDraft({ ...valid, persisted: true }), false);
  assert.equal(validateReviewedMealDraft({ ...valid, originalFilename: "private.jpg" }), false);
  assert.equal(validateReviewedMealDraft({ ...valid, providerPayload: {} }), false);
  assert.equal(validateReviewedMealDraft({ ...valid, title: "T".repeat(101) }), false);
  assert.equal(validateReviewedMealDraft({
    ...valid,
    nutrition: { ...valid.nutrition, calories: 10_001 }
  }), false);
});

test("reviewed-draft module has no output, network, storage, file, provider, or save side effects", async () => {
  const source = await readFile(path.join(__dirname, "../js/meal-snapshot-reviewed-draft.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(source, /document\.|querySelector|addEventListener|dispatchEvent|CustomEvent/i);
  assert.doesNotMatch(source, /FileReader|readAsDataURL|arrayBuffer\s*\(|\.text\s*\(|\.stream\s*\(|createObjectURL|canvas|base64/i);
  assert.doesNotMatch(source, /fetch\s*\(|XMLHttpRequest|createMealSnapshotClient|localStorage|sessionStorage|indexedDB|caches\.|document\.cookie/i);
  assert.doesNotMatch(source, /firebase|firestore|gemini|openai|usda|authorization|bearer/i);
  assert.doesNotMatch(source, /saveReviewed|persistReviewed|sendReviewed|uploadReviewed/i);
  assert.doesNotMatch(renderSource, /buildReviewedDraft\s*\(/);
  assert.match(html, /js\/meal-snapshot-reviewed-draft\.js/);
  assert.match(config, /mealSnapshotBeta\s*:\s*\{[\s\S]*?enabled\s*:\s*false/);
  assert.match(config, /allowConfirmedSave\s*:\s*false/);
});
