"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var ElicitationForm_exports = {};
__export(ElicitationForm_exports, {
  ElicitationForm: () => ElicitationForm
});
module.exports = __toCommonJS(ElicitationForm_exports);
var import_jsx_runtime = require("react/jsx-runtime");
var import_react = require("react");
var import_ink = require("ink");
var import_wrap_ansi = __toESM(require("wrap-ansi"), 1);
var import_useTerminalSize = require("../hooks/useTerminalSize.js");
var import_elicitationSchema = require("../utils/elicitationSchema.js");
function hasOwn(obj, key) {
  return Object.prototype.hasOwnProperty.call(obj, key);
}
function getDisplayValue(value) {
  if (value === void 0 || value === null || value === "") return "\u2014";
  if (Array.isArray(value)) return value.length > 0 ? value.join(", ") : "\u2014";
  if (value === true) return "Yes";
  if (value === false) return "No";
  return String(value ?? "");
}
function clamp(value, min, max) {
  return Math.max(min, Math.min(max, value));
}
const ElicitationForm = (0, import_react.forwardRef)(
  ({ metadata, onSubmit, onCancel }, ref) => {
    const { rows: terminalRows, columns: terminalColumns } = (0, import_useTerminalSize.useTerminalSize)();
    const headerLineCount = 1;
    const stepHeaderLineCount = 2;
    const spacerAfterStepLineCount = 1;
    const questionLineCount = 2;
    const helpLineCount = 2;
    const errorLineCount = 1;
    const questionHeaderHeight = headerLineCount + stepHeaderLineCount + spacerAfterStepLineCount + questionLineCount + helpLineCount + errorLineCount;
    const reviewHeaderHeight = headerLineCount + spacerAfterStepLineCount;
    const maxHeaderHeight = Math.max(questionHeaderHeight, reviewHeaderHeight);
    const footerHeight = 1;
    const minContentHeight = 4;
    const viewportHeight = (0, import_react.useMemo)(() => {
      const reservedRows = 8;
      const minViewportHeight = maxHeaderHeight + footerHeight + minContentHeight;
      const maxHeight = Math.max(minViewportHeight, terminalRows - reservedRows);
      const desired = Math.max(minViewportHeight, Math.floor(terminalRows * 0.6));
      return Math.min(maxHeight, desired);
    }, [footerHeight, maxHeaderHeight, minContentHeight, terminalRows]);
    const availableWidth = Math.max(20, terminalColumns - 2);
    const fields = (0, import_react.useMemo)(() => {
      return (0, import_elicitationSchema.parseElicitationSchema)(metadata.schema);
    }, [metadata.schema]);
    const wrapClampedLines = (0, import_react.useCallback)(
      (text, maxLines) => {
        if (maxLines <= 0) return [];
        const wrapped = (0, import_wrap_ansi.default)(text, availableWidth, {
          hard: true,
          wordWrap: true,
          trim: false
        });
        const rawLines = wrapped.length > 0 ? wrapped.split("\n") : [""];
        const didTruncate = rawLines.length > maxLines;
        const lines = rawLines.slice(0, maxLines);
        if (didTruncate && lines.length > 0) {
          const lastIndex = lines.length - 1;
          const lastLine = (lines[lastIndex] ?? "").replace(/\s+$/, "");
          const safe = lastLine.length > 0 ? `${lastLine.slice(0, Math.max(0, lastLine.length - 1))}\u2026` : "\u2026";
          lines[lastIndex] = safe;
        }
        while (lines.length < maxLines) {
          lines.push("");
        }
        return lines;
      },
      [availableWidth]
    );
    const [activeFieldIndex, setActiveFieldIndex] = (0, import_react.useState)(0);
    const [formData, setFormData] = (0, import_react.useState)({});
    const [draftInputs, setDraftInputs] = (0, import_react.useState)({});
    const [enumIndex, setEnumIndex] = (0, import_react.useState)(0);
    const [arraySelections, setArraySelections] = (0, import_react.useState)(/* @__PURE__ */ new Set());
    const [errors, setErrors] = (0, import_react.useState)({});
    const [isReviewing, setIsReviewing] = (0, import_react.useState)(false);
    const [reviewScrollTop, setReviewScrollTop] = (0, import_react.useState)(0);
    const activeField = fields[activeFieldIndex];
    const contentHeight = (0, import_react.useMemo)(() => {
      const activeHeaderHeight = isReviewing ? reviewHeaderHeight : questionHeaderHeight;
      return Math.max(1, viewportHeight - activeHeaderHeight - footerHeight);
    }, [footerHeight, isReviewing, questionHeaderHeight, reviewHeaderHeight, viewportHeight]);
    const updateField = (0, import_react.useCallback)((name, value2) => {
      setFormData((prev) => ({ ...prev, [name]: value2 }));
      setErrors((prev) => {
        if (!hasOwn(prev, name)) return prev;
        const next = { ...prev };
        delete next[name];
        return next;
      });
    }, []);
    const goToFieldIndex = (0, import_react.useCallback)(
      (index, data = formData) => {
        if (index < 0 || index >= fields.length) return;
        setActiveFieldIndex(index);
        const field = fields[index];
        if (!field) return;
        if (field.type === "boolean") {
          const currentValue = data[field.name];
          setEnumIndex(currentValue === false ? 1 : 0);
          setArraySelections(/* @__PURE__ */ new Set());
          return;
        }
        if (field.type === "enum") {
          const values = field.enumValues ?? [];
          const currentValue = data[field.name];
          const currentIndex = values.findIndex((v) => v === currentValue);
          setEnumIndex(currentIndex >= 0 ? currentIndex : 0);
          setArraySelections(/* @__PURE__ */ new Set());
          return;
        }
        if (field.type === "array-enum") {
          const values = field.enumValues ?? [];
          const currentValue = data[field.name];
          const currentValues = Array.isArray(currentValue) ? currentValue : [];
          const selections = /* @__PURE__ */ new Set();
          for (const selectedValue of currentValues) {
            const selectedIndex = values.findIndex((v) => v === selectedValue);
            if (selectedIndex >= 0) selections.add(selectedIndex);
          }
          setArraySelections(selections);
          const firstSelected = selections.values().next().value;
          const maxIndex = Math.max(0, values.length - 1);
          setEnumIndex(Math.min(firstSelected ?? 0, maxIndex));
          return;
        }
        setEnumIndex(0);
        setArraySelections(/* @__PURE__ */ new Set());
      },
      [fields, formData]
    );
    const nextField = (0, import_react.useCallback)(() => {
      if (activeFieldIndex < fields.length - 1) {
        goToFieldIndex(activeFieldIndex + 1);
      }
    }, [activeFieldIndex, fields.length, goToFieldIndex]);
    const prevField = (0, import_react.useCallback)(() => {
      if (activeFieldIndex > 0) {
        goToFieldIndex(activeFieldIndex - 1);
      }
    }, [activeFieldIndex, goToFieldIndex]);
    const handleSubmit = (0, import_react.useCallback)(
      (currentFieldValue) => {
        const newErrors = {};
        const finalFormData = currentFieldValue ? { ...formData, [currentFieldValue.name]: currentFieldValue.value } : { ...formData };
        for (const field of fields) {
          if (!hasOwn(draftInputs, field.name)) continue;
          const rawDraft = draftInputs[field.name] ?? "";
          if (field.type === "number") {
            const trimmed = rawDraft.trim();
            if (trimmed === "") {
              finalFormData[field.name] = "";
              continue;
            }
            const parsed = Number(trimmed);
            if (Number.isNaN(parsed)) {
              newErrors[field.name] = "Invalid number";
              continue;
            }
            finalFormData[field.name] = parsed;
          }
          if (field.type === "string") {
            finalFormData[field.name] = rawDraft;
          }
        }
        for (const field of fields) {
          if (!field.required) continue;
          const value2 = finalFormData[field.name];
          if (field.type === "array-enum") {
            if (!Array.isArray(value2) || value2.length === 0) {
              newErrors[field.name] = "Required";
            }
            continue;
          }
          if (value2 === void 0 || value2 === null || value2 === "") {
            newErrors[field.name] = "Required";
          }
        }
        if (Object.keys(newErrors).length > 0) {
          setErrors(newErrors);
          const firstErrorField = fields.findIndex((f) => newErrors[f.name]);
          if (firstErrorField >= 0) {
            goToFieldIndex(firstErrorField, finalFormData);
          }
          setIsReviewing(false);
          return;
        }
        setFormData(finalFormData);
        setReviewScrollTop(0);
        setIsReviewing(true);
      },
      [draftInputs, fields, formData, goToFieldIndex]
    );
    const confirmSubmit = (0, import_react.useCallback)(() => {
      onSubmit(formData);
    }, [formData, onSubmit]);
    (0, import_react.useImperativeHandle)(
      ref,
      () => ({
        handleInput: (input, key) => {
          if (isReviewing) {
            const maxScrollTop = Math.max(0, fields.length - contentHeight);
            if (key.return) {
              confirmSubmit();
              return true;
            }
            if (key.backspace || key.delete) {
              setIsReviewing(false);
              goToFieldIndex(Math.max(0, fields.length - 1));
              return true;
            }
            if (key.leftArrow || key.tab && key.shift) {
              setIsReviewing(false);
              goToFieldIndex(Math.max(0, fields.length - 1));
              return true;
            }
            if (key.upArrow) {
              setReviewScrollTop((prev) => Math.max(0, prev - 1));
              return true;
            }
            if (key.downArrow) {
              setReviewScrollTop((prev) => Math.min(maxScrollTop, prev + 1));
              return true;
            }
            if (key.pageUp) {
              setReviewScrollTop((prev) => Math.max(0, prev - 5));
              return true;
            }
            if (key.pageDown) {
              setReviewScrollTop((prev) => Math.min(maxScrollTop, prev + 5));
              return true;
            }
            if (key.escape) {
              onCancel();
              return true;
            }
            return false;
          }
          if (key.escape) {
            onCancel();
            return true;
          }
          if (!activeField) return false;
          if (key.leftArrow || key.tab && key.shift) {
            prevField();
            return true;
          }
          if (key.rightArrow || key.tab && !key.shift) {
            if (activeFieldIndex === fields.length - 1) {
              handleSubmit();
            } else {
              nextField();
            }
            return true;
          }
          const setDraftValue = (updater) => {
            const hadError = hasOwn(errors, activeField.name);
            setDraftInputs((prev) => {
              const current = hasOwn(prev, activeField.name) ? prev[activeField.name] ?? "" : (() => {
                const existing = formData[activeField.name];
                return existing === void 0 || existing === null ? "" : String(existing);
              })();
              return { ...prev, [activeField.name]: updater(current) };
            });
            if (hadError) {
              setErrors((prevErrors) => {
                const nextErrors = { ...prevErrors };
                delete nextErrors[activeField.name];
                return nextErrors;
              });
            }
          };
          switch (activeField.type) {
            case "boolean": {
              if (key.upArrow) {
                setEnumIndex((prev) => clamp(prev - 1, 0, 1));
                return true;
              }
              if (key.downArrow) {
                setEnumIndex((prev) => clamp(prev + 1, 0, 1));
                return true;
              }
              if (input === " ") {
                setEnumIndex((prev) => prev === 0 ? 1 : 0);
                return true;
              }
              if (key.return) {
                const nextValue = enumIndex === 0;
                updateField(activeField.name, nextValue);
                if (activeFieldIndex === fields.length - 1) {
                  handleSubmit({ name: activeField.name, value: nextValue });
                } else {
                  nextField();
                }
                return true;
              }
              break;
            }
            case "enum": {
              const values = activeField.enumValues || [];
              if (key.upArrow) {
                setEnumIndex((prev) => prev > 0 ? prev - 1 : values.length - 1);
                return true;
              }
              if (key.downArrow) {
                setEnumIndex((prev) => prev < values.length - 1 ? prev + 1 : 0);
                return true;
              }
              if (key.return) {
                const selectedValue = values[enumIndex];
                updateField(activeField.name, selectedValue);
                if (activeFieldIndex === fields.length - 1) {
                  handleSubmit({ name: activeField.name, value: selectedValue });
                } else {
                  nextField();
                }
                return true;
              }
              break;
            }
            case "array-enum": {
              const values = activeField.enumValues || [];
              if (key.upArrow) {
                setEnumIndex((prev) => prev > 0 ? prev - 1 : values.length - 1);
                return true;
              }
              if (key.downArrow) {
                setEnumIndex((prev) => prev < values.length - 1 ? prev + 1 : 0);
                return true;
              }
              if (input === " ") {
                const nextSelections = new Set(arraySelections);
                if (nextSelections.has(enumIndex)) nextSelections.delete(enumIndex);
                else nextSelections.add(enumIndex);
                setArraySelections(nextSelections);
                const selected = Array.from(nextSelections).map((i) => values[i]);
                updateField(activeField.name, selected);
                return true;
              }
              if (key.return) {
                const selected = Array.from(arraySelections).map((i) => values[i]);
                if (activeFieldIndex === fields.length - 1) {
                  handleSubmit({ name: activeField.name, value: selected });
                } else {
                  nextField();
                }
                return true;
              }
              break;
            }
            case "string":
            case "number": {
              const hasDraft = hasOwn(draftInputs, activeField.name);
              const rawInput = hasDraft ? draftInputs[activeField.name] ?? "" : (() => {
                const existing = formData[activeField.name];
                return existing === void 0 || existing === null ? "" : String(existing);
              })();
              if (key.return) {
                let value2 = hasDraft ? rawInput : formData[activeField.name];
                if (activeField.type === "number" && hasDraft) {
                  const trimmed = rawInput.trim();
                  if (trimmed === "") {
                    value2 = "";
                  } else {
                    const parsed = Number(trimmed);
                    if (Number.isNaN(parsed)) {
                      setErrors((prev) => ({
                        ...prev,
                        [activeField.name]: "Invalid number"
                      }));
                      return true;
                    }
                    value2 = parsed;
                  }
                }
                if (hasDraft) {
                  updateField(activeField.name, value2);
                }
                if (activeFieldIndex === fields.length - 1) {
                  handleSubmit(
                    value2 !== void 0 ? { name: activeField.name, value: value2 } : void 0
                  );
                } else {
                  nextField();
                }
                return true;
              }
              if (key.backspace || key.delete) {
                setDraftValue((prev) => prev.slice(0, -1));
                return true;
              }
              if (input && !key.ctrl && !key.meta) {
                if (activeField.type === "number") {
                  const filtered = input.replace(/[^\d.-]/g, "");
                  if (filtered.length > 0) {
                    setDraftValue((prev) => prev + filtered);
                  }
                } else {
                  setDraftValue((prev) => prev + input);
                }
                return true;
              }
              break;
            }
          }
          return false;
        }
      }),
      [
        activeField,
        activeFieldIndex,
        arraySelections,
        contentHeight,
        confirmSubmit,
        draftInputs,
        enumIndex,
        errors,
        fields,
        formData,
        handleSubmit,
        isReviewing,
        goToFieldIndex,
        nextField,
        onCancel,
        prevField,
        reviewScrollTop,
        updateField
      ]
    );
    if (fields.length === 0) {
      return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Box, { flexDirection: "column", paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "red", children: "Invalid form schema" }) });
    }
    const isAnswered = (field) => {
      if (field.type === "boolean") return hasOwn(formData, field.name);
      if (field.type === "enum") return hasOwn(formData, field.name);
      if (field.type === "array-enum") {
        const value3 = formData[field.name];
        return Array.isArray(value3) && value3.length > 0;
      }
      const draft = draftInputs[field.name];
      if (typeof draft === "string" && draft.trim() !== "") return true;
      const value2 = formData[field.name];
      return value2 !== void 0 && value2 !== null && value2 !== "";
    };
    if (!isReviewing && !activeField) {
      return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Box, { flexDirection: "column", paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "red", children: "Invalid form state" }) });
    }
    const value = activeField ? formData[activeField.name] : void 0;
    const currentInput = activeField && hasOwn(draftInputs, activeField.name) ? draftInputs[activeField.name] ?? "" : value === void 0 || value === null ? "" : String(value);
    const errorText = activeField ? errors[activeField.name] ?? "" : "";
    const renderContent = () => {
      if (isReviewing) {
        return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
          import_ink.Box,
          {
            overflowY: "scroll",
            overflowX: "hidden",
            scrollTop: reviewScrollTop,
            scrollbarThumbColor: "gray",
            flexDirection: "column",
            height: contentHeight,
            paddingRight: 1,
            children: fields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { wrap: "truncate-end", children: [
              field.question,
              ": ",
              getDisplayValue(formData[field.name])
            ] }, field.name))
          }
        );
      }
      if (!activeField) return null;
      if (activeField.type === "string" || activeField.type === "number") {
        return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Box, { height: contentHeight, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { wrap: "truncate-end", children: [
          /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "cyan", children: "> " }),
          currentInput || /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "gray", children: "Type your answer\u2026" }),
          /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "cyan", children: "\u258B" })
        ] }) });
      }
      const options = [];
      if (activeField.type === "boolean") {
        options.push({
          key: "yes",
          label: "Yes",
          isFocused: enumIndex === 0,
          isSelected: false
        });
        options.push({
          key: "no",
          label: "No",
          isFocused: enumIndex === 1,
          isSelected: false
        });
      }
      if ((activeField.type === "enum" || activeField.type === "array-enum") && activeField.enumValues) {
        activeField.enumValues.forEach((opt, i) => {
          const isFocused = i === enumIndex;
          const isSelected = activeField.type === "enum" ? false : arraySelections.has(i);
          options.push({
            key: `${String(opt)}-${i}`,
            label: String(opt),
            isFocused,
            isSelected
          });
        });
      }
      const focusedIndex = Math.max(
        0,
        options.findIndex((o) => o.isFocused)
      );
      const maxScrollTop = Math.max(0, options.length - contentHeight);
      const targetScrollTop = clamp(
        focusedIndex - Math.floor(contentHeight / 2),
        0,
        maxScrollTop
      );
      return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
        import_ink.Box,
        {
          overflowY: "scroll",
          overflowX: "hidden",
          scrollbarThumbColor: "gray",
          height: contentHeight,
          scrollTop: targetScrollTop,
          flexDirection: "column",
          paddingRight: 1,
          children: options.map((opt) => {
            const prefix = opt.isFocused ? "\u25B6 " : "  ";
            const mark = activeField.type === "array-enum" ? opt.isSelected ? "[\u2713] " : "[ ] " : "";
            return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
              import_ink.Text,
              {
                color: opt.isFocused ? "cyan" : "gray",
                wrap: "truncate-end",
                children: [
                  prefix,
                  mark,
                  opt.label
                ]
              },
              opt.key
            );
          })
        }
      );
    };
    const hintLine = (() => {
      if (isReviewing) return "Enter submit \u2022 Backspace/\u2190 edit \u2022 \u2191\u2193 scroll \u2022 Esc cancel";
      switch (activeField?.type) {
        case "string":
        case "number":
          return "Type to answer \u2022 Enter next \u2022 \u2190/\u2192 question \u2022 Esc cancel";
        case "array-enum":
          return "\u2191/\u2193 option \u2022 Space toggle \u2022 Enter next \u2022 \u2190/\u2192 question \u2022 Esc cancel";
        default:
          return "\u2191/\u2193 option \u2022 Enter select \u2022 \u2190/\u2192 question \u2022 Esc cancel";
      }
    })();
    const headerText = isReviewing ? "\u{1F4DD} Review your answers" : `\u{1F4DD} Please answer ${fields.length === 1 ? "this" : "these"} ${fields.length} ${fields.length === 1 ? "question" : "questions"}.`;
    const stepText = (() => {
      if (isReviewing) {
        return "";
      }
      if (!activeField) return "";
      return `Question ${activeFieldIndex + 1}/${fields.length}: ${activeField.stepLabel}`;
    })();
    const questionText = !isReviewing && activeField ? `${activeField.question}${activeField.required ? "*" : ""}` : "";
    const helpText = !isReviewing && activeField?.helpText ? activeField.helpText : "";
    const errorLineText = !isReviewing ? errorText : "";
    const headerLines = wrapClampedLines(headerText, headerLineCount);
    const stepLines = wrapClampedLines(stepText, stepHeaderLineCount);
    const questionLines = wrapClampedLines(questionText, questionLineCount);
    const helpLines = wrapClampedLines(helpText, helpLineCount);
    const errorLines = wrapClampedLines(errorLineText, errorLineCount);
    return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Box, { flexDirection: "column", paddingX: 0, height: viewportHeight, children: [
      headerLines.map((line, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "yellowBright", bold: true, wrap: "truncate-end", children: line || " " }, `header-${index}`)),
      !isReviewing && stepLines.map((line, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "gray", dimColor: true, wrap: "truncate-end", children: line || " " }, `step-${index}`)),
      Array.from({ length: spacerAfterStepLineCount }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { wrap: "truncate-end", children: " " }, `spacer-step-${index}`)),
      !isReviewing && questionLines.map((line, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "white", bold: true, wrap: "truncate-end", children: line || " " }, `question-${index}`)),
      !isReviewing && helpLines.map((line, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "gray", dimColor: true, wrap: "truncate-end", children: line || " " }, `help-${index}`)),
      !isReviewing && errorLines.map((line, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "red", wrap: "truncate-end", children: line || " " }, `error-${index}`)),
      renderContent(),
      /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink.Text, { color: "gray", dimColor: true, wrap: "truncate-end", children: hintLine })
    ] });
  }
);
ElicitationForm.displayName = "ElicitationForm";
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  ElicitationForm
});
