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

const { DEFAULT_MAX_IMAGE_BYTES, validateFile } = require("../js/meal-snapshot-service.js");
const {
  MEAL_SNAPSHOT_ESTIMATE_DISCLAIMER,
  createInitialMealSnapshotBetaUiState,
  createMealSnapshotPendingUiState,
  createMealSnapshotUiEvent,
  mapMealSnapshotResultToUiState,
  selectMealSnapshotFallback
} = require("../js/meal-snapshot-ui-state.js");

const DISABLED_FEATURE = Object.freeze({
  enabled: false,
  environment: "dev",
  endpoint: "",
  allowConfirmedSave: false,
  allowedRoles: ["patient"]
});

const ENABLED_TEST_FEATURE = Object.freeze({
  ...DISABLED_FEATURE,
  enabled: true,
  endpoint: "https://local.invalid/meal-snapshot"
});

function makeFile(type = "image/jpeg", size = 16) {
  return { type, size, name: "patient-name-breakfast.jpg" };
}

test("disabled flag keeps the Beta UI path inactive", () => {
  const initial = createInitialMealSnapshotBetaUiState(DISABLED_FEATURE);
  const pending = createMealSnapshotPendingUiState(DISABLED_FEATURE);
  const mapped = mapMealSnapshotResultToUiState(DISABLED_FEATURE, {
    status: "review_needed",
    estimate: { title: "Sensitive meal name" }
  });

  assert.equal(initial.status, "disabled");
  assert.equal(pending.status, "disabled");
  assert.equal(mapped.status, "disabled");
  assert.equal(initial.canSaveConfirmed, false);
});

test("missing file maps to a safe no-file-selected state", () => {
  const validation = validateFile(null, DEFAULT_MAX_IMAGE_BYTES);
  const state = mapMealSnapshotResultToUiState(ENABLED_TEST_FEATURE, validation);
  assert.equal(state.status, "no_file_selected");
  assert.equal(state.canManualEntry, true);
  assert.equal(state.canSaveConfirmed, false);
});

test("invalid file type maps to a safe invalid-file state", () => {
  const validation = validateFile(makeFile("image/gif"), DEFAULT_MAX_IMAGE_BYTES);
  const state = mapMealSnapshotResultToUiState(ENABLED_TEST_FEATURE, validation);
  assert.equal(state.status, "invalid_file");
  assert.match(state.message, /JPEG, PNG, or WebP/);
  assert.equal(state.canSaveConfirmed, false);
});

test("oversized file maps to a safe oversized-file state", () => {
  const validation = validateFile(
    makeFile("image/jpeg", DEFAULT_MAX_IMAGE_BYTES + 1),
    DEFAULT_MAX_IMAGE_BYTES
  );
  const state = mapMealSnapshotResultToUiState(ENABLED_TEST_FEATURE, validation);
  assert.equal(state.status, "oversized_file");
  assert.equal(state.canManualEntry, true);
  assert.equal(state.canSaveConfirmed, false);
});

test("provider unavailable exposes retry, manual entry, and note-only fallbacks", () => {
  const state = mapMealSnapshotResultToUiState(ENABLED_TEST_FEATURE, {
    status: "provider_unavailable",
    providerStatus: "provider_unavailable",
    manualFallback: {
      available: true,
      actions: ["retry", "manual_entry", "save_note_only"],
      message: "Raw provider message that must not be copied."
    },
    rawProviderResponse: "must-not-pass-through"
  });

  assert.equal(state.status, "retry_available");
  assert.equal(state.canRetry, true);
  assert.equal(state.canManualEntry, true);
  assert.equal(state.canNoteOnly, true);
  assert.equal(state.canSaveConfirmed, false);
  assert.equal(JSON.stringify(state).includes("Raw provider"), false);
  assert.equal(JSON.stringify(state).includes("must-not-pass-through"), false);
});

test("upload pending, manual entry, note-only, and safe generic error states exist", () => {
  const pending = createMealSnapshotPendingUiState(ENABLED_TEST_FEATURE);
  const manual = selectMealSnapshotFallback(ENABLED_TEST_FEATURE, "manual_entry");
  const noteOnly = selectMealSnapshotFallback(ENABLED_TEST_FEATURE, "save_note_only");
  const generic = mapMealSnapshotResultToUiState(ENABLED_TEST_FEATURE, { status: "unexpected_state" });

  assert.equal(pending.status, "upload_pending");
  assert.match(pending.message, /AI-assisted estimate/);
  assert.equal(manual.status, "manual_entry");
  assert.equal(noteOnly.status, "note_only");
  assert.equal(generic.status, "generic_error");
});

test("draft estimate remains labeled as estimated and review-required", () => {
  const state = mapMealSnapshotResultToUiState(ENABLED_TEST_FEATURE, {
    status: "review_needed",
    providerStatus: "mock_success",
    requiresUserReview: true,
    estimate: {
      title: "Sensitive meal title",
      calories: 999,
      ingredients: [{ name: "Sensitive ingredient" }]
    }
  });

  assert.equal(state.status, "draft_estimate");
  assert.equal(state.title, "Estimated Meal Snapshot");
  assert.equal(state.requiresReview, true);
  assert.equal(state.canSaveConfirmed, false);
  assert.equal(state.disclaimer, MEAL_SNAPSHOT_ESTIMATE_DISCLAIMER);
  assert.equal(JSON.stringify(state).includes("Sensitive meal title"), false);
  assert.equal(JSON.stringify(state).includes("999"), false);
});

test("UI event payload is allowlisted and excludes filenames and meal/provider content", () => {
  const state = mapMealSnapshotResultToUiState(ENABLED_TEST_FEATURE, {
    status: "provider_unavailable",
    filename: "patient-name-breakfast.jpg",
    token: "secret-token",
    estimate: { title: "Private meal", calories: 700 },
    rawProviderResponse: "raw response"
  });
  const event = createMealSnapshotUiEvent(state, {
    filename: "patient-name-breakfast.jpg",
    token: "secret-token",
    nutritionText: "700 calories"
  });
  assert.deepEqual(Object.keys(event).sort(), [
    "canManualEntry",
    "canNoteOnly",
    "canRetry",
    "canSaveConfirmed",
    "event",
    "status"
  ]);
  const serialized = JSON.stringify(event);
  assert.equal(serialized.includes("patient-name"), false);
  assert.equal(serialized.includes("secret-token"), false);
  assert.equal(serialized.includes("Private meal"), false);
  assert.equal(serialized.includes("700"), false);
  assert.equal(serialized.includes("raw response"), false);
});

test("confirmed save is unreachable even if a caller supplies an unsafe flag override", () => {
  const unsafeFeature = { ...ENABLED_TEST_FEATURE, allowConfirmedSave: true };
  const states = [
    createInitialMealSnapshotBetaUiState(unsafeFeature),
    createMealSnapshotPendingUiState(unsafeFeature),
    mapMealSnapshotResultToUiState(unsafeFeature, { status: "review_needed" }),
    selectMealSnapshotFallback(unsafeFeature, "manual_entry"),
    selectMealSnapshotFallback(unsafeFeature, "save_note_only")
  ];
  assert.equal(states.every((state) => state.canSaveConfirmed === false), true);
});

test("Phase 1B UI-state source has no DOM, network, persistence, image reads, or base64 path", async () => {
  const source = await readFile(path.join(__dirname, "../js/meal-snapshot-ui-state.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\.|window\.|querySelector|addEventListener|FileReader|arrayBuffer\s*\(|readAsDataURL|btoa\s*\(|base64/i);
  assert.doesNotMatch(source, /fetch\s*\(|XMLHttpRequest|localStorage|sessionStorage|indexedDB/i);
  assert.doesNotMatch(source, /firebase|firestore|gemini|openai|usda|authorization|bearer/i);
  assert.doesNotMatch(source, /saveSnapshot|persistSnapshot|writeSnapshot/i);
  assert.equal(html.includes("js/meal-snapshot-ui-state.js"), true);
  assert.equal(html.includes("js/meal-snapshot-ui-renderer.js"), true);
  assert.equal(html.includes("js/meal-snapshot-service.js"), true);
  assert.match(config, /mealSnapshotBeta\s*:\s*\{[\s\S]*?enabled\s*:\s*false/);
  assert.match(config, /allowConfirmedSave\s*:\s*false/);
});
