(function (root, factory) {
  const controller = factory();
  if (typeof module === "object" && module.exports) {
    module.exports = controller;
  } else {
    root.MealSnapshotUiController = controller;
  }
}(typeof globalThis !== "undefined" ? globalThis : this, function () {
  "use strict";

  const ACCEPTED_IMAGE_TYPES = "image/jpeg,image/png,image/webp";

  function createElement(documentRef, tagName, className, text) {
    const node = documentRef.createElement(tagName);
    if (className) node.className = className;
    if (text !== undefined && text !== null) node.textContent = String(text);
    return node;
  }

  function inactiveController() {
    return Object.freeze({
      mounted: false,
      input: null,
      getState: () => Object.freeze({ status: "disabled", canSaveConfirmed: false }),
      getDraft: () => null,
      buildReviewedDraft: () => null,
      destroy: () => undefined
    });
  }

  function mountMealSnapshotBetaController(options = {}) {
    const featureConfig = options.featureConfig || {};
    if (featureConfig.enabled !== true) return inactiveController();

    const container = options.container;
    const uiStateApi = options.uiStateApi;
    const rendererApi = options.rendererApi;
    const mockEstimateApi = options.mockEstimateApi;
    const reviewRendererApi = options.reviewRendererApi;
    const reviewedDraftApi = options.reviewedDraftApi;
    const fallbackRendererApi = options.fallbackRendererApi;
    const validateFile = options.validateFile;
    const maxImageBytes = options.maxImageBytes;
    const onEvent = options.onEvent;
    const documentRef = options.documentRef || container?.ownerDocument;

    if (!container || typeof container.replaceChildren !== "function") {
      throw new TypeError("Meal Snapshot Beta requires a target DOM element.");
    }
    if (!documentRef || typeof documentRef.createElement !== "function") {
      throw new TypeError("Meal Snapshot Beta requires a document interface.");
    }
    if (!uiStateApi || !rendererApi || !mockEstimateApi || !reviewRendererApi || !reviewedDraftApi || !fallbackRendererApi || typeof validateFile !== "function") {
      throw new TypeError("Meal Snapshot Beta requires state, renderer, mock estimate, review, fallback, reviewed draft, and validation interfaces.");
    }

    const shell = createElement(documentRef, "section", "meal-snapshot-beta-controller");
    shell.setAttribute("aria-label", "Meal Snapshot Beta local photo validation");
    const uploadArea = createElement(documentRef, "div", "meal-snapshot-beta-upload-control");
    const inputId = "mealSnapshotBetaFile";
    const input = createElement(documentRef, "input", "meal-snapshot-beta-file-input");
    input.type = "file";
    input.accept = ACCEPTED_IMAGE_TYPES;
    input.setAttribute("id", inputId);
    input.setAttribute("aria-describedby", "mealSnapshotBetaFileHelp");
    const label = createElement(documentRef, "label", "meal-snapshot-beta-file-label", "Choose a meal photo");
    label.setAttribute("for", inputId);
    const help = createElement(
      documentRef,
      "p",
      "meal-snapshot-beta-file-help",
      "JPEG, PNG, or WebP. This step checks the photo locally only."
    );
    help.setAttribute("id", "mealSnapshotBetaFileHelp");
    const stateHost = createElement(documentRef, "div", "meal-snapshot-beta-state-host");
    uploadArea.append(input, label, help);
    shell.append(uploadArea, stateHost);
    container.replaceChildren(shell);

    let currentState = uiStateApi.createInitialMealSnapshotBetaUiState(featureConfig);
    let currentDraft = null;
    let draftEdited = false;
    let fallbackDraft = null;

    const clearFallbackDraft = () => {
      fallbackDraft = null;
    };

    const emit = () => {
      if (typeof onEvent === "function") onEvent(uiStateApi.createMealSnapshotUiEvent(currentState));
    };

    const renderState = (nextState) => {
      currentState = nextState;
      rendererApi.renderMealSnapshotBetaIfEnabled(stateHost, featureConfig, currentState, {
        onRetry: () => renderState(uiStateApi.createInitialMealSnapshotBetaUiState(featureConfig)),
        onManualEntry: () => renderManualEntry(),
        onNoteOnly: () => renderNoteOnly(),
        onReviewMockEstimate: () => renderReview()
      });
      emit();
      return currentState;
    };

    const emptyManualDraft = () => ({
      kind: "manual_entry",
      title: "",
      notes: "",
      nutrition: { calories: null, protein: null, carbs: null, fat: null }
    });

    const emptyNoteDraft = () => ({ kind: "note_only", note: "" });

    const clearAndReturn = () => {
      clearFallbackDraft();
      renderState(uiStateApi.createInitialMealSnapshotBetaUiState(featureConfig));
    };

    const applyFallbackEdit = (edit) => {
      if (!fallbackDraft || !edit || typeof edit.field !== "string") return;
      if (fallbackDraft.kind === "manual_entry" && ["title", "notes"].includes(edit.field)) {
        const limit = edit.field === "title" ? 100 : 1000;
        fallbackDraft[edit.field] = String(edit.value || "").slice(0, limit);
        return;
      }
      if (fallbackDraft.kind === "manual_entry" && ["calories", "protein", "carbs", "fat"].includes(edit.field)) {
        if (String(edit.value || "").trim() === "") {
          fallbackDraft.nutrition[edit.field] = null;
          return;
        }
        const numericValue = Number(edit.value);
        if (Number.isFinite(numericValue) && numericValue >= 0 && numericValue <= 10_000) {
          fallbackDraft.nutrition[edit.field] = numericValue;
        }
        return;
      }
      if (fallbackDraft.kind === "note_only" && edit.field === "note") {
        fallbackDraft.note = String(edit.value || "").slice(0, 1000);
      }
    };

    const renderManualEntry = (reset = false) => {
      if (!fallbackDraft || fallbackDraft.kind !== "manual_entry" || reset) fallbackDraft = emptyManualDraft();
      currentState = uiStateApi.selectMealSnapshotFallback(featureConfig, "manual_entry");
      fallbackRendererApi.renderManualEntry(stateHost, fallbackDraft, {
        documentRef,
        onEdit: applyFallbackEdit,
        onReset: () => renderManualEntry(true),
        onBack: clearAndReturn
      });
      emit();
      return currentState;
    };

    const renderNoteOnly = (reset = false) => {
      if (!fallbackDraft || fallbackDraft.kind !== "note_only" || reset) fallbackDraft = emptyNoteDraft();
      currentState = uiStateApi.selectMealSnapshotFallback(featureConfig, "save_note_only");
      fallbackRendererApi.renderNoteOnly(stateHost, fallbackDraft, {
        documentRef,
        onEdit: applyFallbackEdit,
        onReset: () => renderNoteOnly(true),
        onBack: clearAndReturn
      });
      emit();
      return currentState;
    };

    const applyDraftEdit = (edit) => {
      if (!currentDraft || !edit || typeof edit.field !== "string") return;
      if (edit.field === "title") {
        currentDraft.title = String(edit.value || "").slice(0, 100);
        draftEdited = true;
        return;
      }
      if (["calories", "protein", "carbs", "fat"].includes(edit.field)) {
        const numericValue = Number(edit.value);
        if (Number.isFinite(numericValue) && numericValue >= 0 && numericValue <= 10_000) {
          currentDraft[edit.field] = numericValue;
          draftEdited = true;
        }
      }
    };

    const applyIngredientEdit = (edit) => {
      if (!currentDraft || !edit || !["name", "amount"].includes(edit.field)) return;
      const safeValue = String(edit.value || "").slice(0, 120);
      currentDraft.ingredients = currentDraft.ingredients.map((ingredient) => (
        ingredient.id === edit.id ? { ...ingredient, [edit.field]: safeValue } : ingredient
      ));
      draftEdited = true;
    };

    const renderReview = (reset = false) => {
      clearFallbackDraft();
      if (!currentDraft || reset) {
        currentDraft = mockEstimateApi.createMockMealSnapshotEstimate();
        draftEdited = false;
      }
      currentState = uiStateApi.mapMealSnapshotResultToUiState(featureConfig, { status: "review_needed" });
      reviewRendererApi.renderMealSnapshotMockReview(stateHost, currentDraft, {
        documentRef,
        onEdit: applyDraftEdit,
        onIngredientEdit: applyIngredientEdit,
        onReset: () => renderReview(true),
        onBack: () => {
          currentDraft = null;
          draftEdited = false;
          renderState(uiStateApi.createMealSnapshotLocalFileReadyUiState(featureConfig));
        }
      });
      emit();
      return currentState;
    };

    const handleSelection = (event) => {
      const selectedFile = event?.currentTarget?.files?.[0] || null;
      const validationResult = validateFile(selectedFile, maxImageBytes);
      // Clear immediately so the browser does not retain or display selection metadata.
      event.currentTarget.value = "";
      if (validationResult) {
        clearFallbackDraft();
        currentDraft = null;
        draftEdited = false;
        renderState(uiStateApi.mapMealSnapshotResultToUiState(featureConfig, validationResult));
        return;
      }
      currentDraft = null;
      draftEdited = false;
      clearFallbackDraft();
      renderState(uiStateApi.createMealSnapshotLocalFileReadyUiState(featureConfig));
    };

    input.addEventListener("change", handleSelection);
    renderState(currentState);

    return Object.freeze({
      mounted: true,
      input,
      getState: () => currentState,
      getDraft: () => currentDraft ? structuredClone(currentDraft) : null,
      buildReviewedDraft: () => currentDraft
        ? reviewedDraftApi.createReviewedMealDraft(currentDraft, { userEdited: draftEdited })
        : null,
      destroy() {
        input.removeEventListener("change", handleSelection);
        input.value = "";
        currentDraft = null;
        draftEdited = false;
        clearFallbackDraft();
        stateHost.replaceChildren();
      }
    });
  }

  return Object.freeze({
    ACCEPTED_IMAGE_TYPES,
    mountMealSnapshotBetaController
  });
}));
