// @bun
import {
  formatKeyHints
} from "./chunk-bexyahs9.js";
import {
  bind,
  bold,
  box,
  fg,
  getActiveTheme,
  interval,
  onKey,
  signal,
  t,
  text
} from "./chunk-m2h26j5f.js";

// src/components/user-input.ts
var BLINK_MS = 530;
function userInput(renderer, scope, props) {
  const {
    prompt,
    allowTextInput = true,
    placeholder = "Type your response...",
    initialValue = "",
    validate,
    onSubmit,
    onCancel,
    dropdownConfigs = [],
    maxDropdownItems = 10,
    type = "text",
    maskChar = "*"
  } = props;
  const theme = getActiveTheme();
  const width = renderer.width || 80;
  const value = signal(initialValue);
  const cursorVisible = signal(true);
  const error = signal(null);
  const dropdownVisible = signal(false);
  const filtered = signal([]);
  const selectedIdx = signal(0);
  const scrollOffset = signal(0);
  let currentConfig = null;
  let currentTrigger = "";
  const hideDropdown = () => {
    dropdownVisible.set(false);
    filtered.set([]);
    selectedIdx.set(0);
    scrollOffset.set(0);
    currentConfig = null;
    currentTrigger = "";
  };
  const updateDropdown = (input) => {
    for (const config of dropdownConfigs) {
      if (config.trigger === "") {
        const filterText = input.toLowerCase();
        const matches = config.options.filter((o) => o.label.toLowerCase().includes(filterText) || o.value.toLowerCase().includes(filterText));
        filtered.set(matches);
        selectedIdx.set(0);
        scrollOffset.set(0);
        currentTrigger = config.trigger;
        currentConfig = config;
        dropdownVisible.set(true);
        return;
      }
      const triggerIndex = input.lastIndexOf(config.trigger);
      if (triggerIndex !== -1) {
        const afterTrigger = input.slice(triggerIndex + config.trigger.length);
        if (afterTrigger.includes(" ") || afterTrigger.includes(`
`))
          continue;
        if (config.options.some((o) => o.value === config.trigger + afterTrigger))
          continue;
        const filterText = afterTrigger.toLowerCase();
        const matches = config.options.filter((o) => o.label.toLowerCase().includes(filterText) || o.value.toLowerCase().includes(filterText));
        if (matches.length > 0) {
          filtered.set(matches);
          selectedIdx.set(0);
          scrollOffset.set(0);
          currentTrigger = config.trigger;
          currentConfig = config;
          dropdownVisible.set(true);
          return;
        }
      }
    }
    dropdownVisible.set(false);
  };
  const updateScroll = (idx, total) => {
    if (total <= maxDropdownItems) {
      scrollOffset.set(0);
      return;
    }
    if (idx < scrollOffset())
      scrollOffset.set(idx);
    else if (idx >= scrollOffset() + maxDropdownItems)
      scrollOffset.set(idx - maxDropdownItems + 1);
  };
  const submit = (raw) => {
    const finalText = raw.trim();
    if (validate) {
      const err = validate(finalText);
      if (err) {
        error.set(err);
        return;
      }
    }
    error.set(null);
    onSubmit(finalText, false);
  };
  const selectDropdownOption = (option) => {
    if (!currentConfig)
      return;
    const newText = currentTrigger === "" ? option.value : value().slice(0, value().lastIndexOf(currentTrigger)) + option.value;
    if ((currentConfig.behavior || "input") === "submit") {
      hideDropdown();
      submit(newText);
    } else {
      value.set(newText);
      hideDropdown();
      error.set(null);
    }
  };
  const promptLine = text(renderer, t`${fg(theme.accent.cyan)(bold(prompt))}`);
  const inputLine = text(renderer, "");
  scope.add(bind(() => {
    const masked = type === "password" ? maskChar.repeat(value().length) : value();
    const cursor = cursorVisible() ? "\u2588" : " ";
    inputLine.content = masked.length === 0 ? t`${fg(theme.status.success)("> ")}${fg(theme.text.primary)(cursor)} ${fg(theme.text.dimmer)(placeholder)}` : t`${fg(theme.status.success)("> ")}${fg(theme.text.primary)(masked)}${fg(theme.text.primary)(cursor)}`;
  }, [value, cursorVisible]));
  const inputBox = box(renderer, { border: true, borderStyle: "rounded", borderColor: theme.ui.border, width, paddingX: 1, marginBottom: 1 }, [inputLine]);
  const dropdownInner = box(renderer, { flexDirection: "column" });
  const dropdownBox = box(renderer, { border: true, borderStyle: "rounded", borderColor: theme.ui.borderFocus, width, paddingX: 1, marginBottom: 1 }, [dropdownInner]);
  const rebuildDropdown = () => {
    for (const child of dropdownInner.getChildren())
      dropdownInner.remove(child.id);
    const opts = filtered();
    const off = scrollOffset();
    const visible = opts.slice(off, off + maxDropdownItems);
    if (off > 0) {
      dropdownInner.add(text(renderer, t`${fg(theme.text.dim)(`\u2191 ${off} more items above`)}`));
    }
    const maxLabel = visible.reduce((m, o) => Math.max(m, o.label.length), 0);
    const tabStop = Math.ceil((maxLabel + 4) / 4) * 4;
    visible.forEach((opt, vi) => {
      const actual = off + vi;
      const sel = actual === selectedIdx();
      const marker = fg(sel ? theme.accent.cyan : theme.text.dim)(sel ? `${theme.symbols.pointer} ` : "  ");
      const label = sel ? fg(theme.accent.cyan)(bold(opt.label)) : fg(theme.text.primary)(opt.label);
      if (opt.description) {
        const pad = " ".repeat(Math.max(0, tabStop - opt.label.length));
        dropdownInner.add(text(renderer, t`${marker}${label}${fg(theme.text.dim)(`${pad}${opt.description}`)}`));
      } else {
        dropdownInner.add(text(renderer, t`${marker}${label}`));
      }
    });
    if (off + maxDropdownItems < opts.length) {
      dropdownInner.add(text(renderer, t`${fg(theme.text.dim)(`\u2193 ${opts.length - (off + maxDropdownItems)} more items below`)}`));
    }
  };
  scope.add(bind(() => {
    const show = dropdownVisible() && filtered().length > 0;
    dropdownBox.visible = show;
    if (show)
      rebuildDropdown();
  }, [dropdownVisible, filtered, selectedIdx, scrollOffset]));
  const errorLine = text(renderer, "");
  scope.add(bind(() => {
    const err = error();
    errorLine.visible = err !== null;
    if (err)
      errorLine.content = t`${fg(theme.status.error)(bold(`${theme.symbols.cross} ${err}`))}`;
  }, [error]));
  const errorBox = box(renderer, { marginBottom: 1 }, [errorLine]);
  const helpLine = text(renderer, "");
  scope.add(bind(() => {
    const hints = dropdownVisible() ? [
      { key: "\u2191\u2193", label: "navigate" },
      { key: "Enter", label: currentConfig?.behavior === "submit" ? "submit" : "select" },
      { label: "continue typing to filter" }
    ] : [
      { key: "Enter", label: "submit" },
      { key: "Ctrl-W", label: "delete word" }
    ];
    if (onCancel)
      hints.push({ key: "Esc", label: "cancel" });
    else if (dropdownVisible())
      hints.push({ key: "Esc", label: "hide" });
    helpLine.content = t`${fg(theme.text.dim)(formatKeyHints(hints))}`;
  }, [dropdownVisible]));
  interval(scope, BLINK_MS, () => cursorVisible.set(!cursorVisible()));
  if (type !== "password")
    updateDropdown(value());
  scope.add(onKey(renderer, (key) => {
    const printable = key.sequence.length === 1 && !key.ctrl && !key.meta && key.sequence >= " " && key.sequence !== "\x7F";
    if (key.name === "escape") {
      if (onCancel) {
        onCancel();
        return;
      }
      if (dropdownVisible()) {
        hideDropdown();
        return;
      }
    }
    if (key.ctrl && key.name === "w" && allowTextInput) {
      const before = value();
      let pos = before.length - 1;
      while (pos >= 0 && /\s/.test(before[pos]))
        pos--;
      while (pos >= 0 && !/\s/.test(before[pos]))
        pos--;
      const next = before.slice(0, pos + 1);
      value.set(next);
      error.set(null);
      if (type !== "password")
        updateDropdown(next);
      return;
    }
    if (dropdownVisible() && filtered().length > 0) {
      if (key.name === "up") {
        const next = selectedIdx() > 0 ? selectedIdx() - 1 : filtered().length - 1;
        selectedIdx.set(next);
        updateScroll(next, filtered().length);
        return;
      }
      if (key.name === "down") {
        const next = selectedIdx() < filtered().length - 1 ? selectedIdx() + 1 : 0;
        selectedIdx.set(next);
        updateScroll(next, filtered().length);
        return;
      }
      if (key.name === "return") {
        selectDropdownOption(filtered()[selectedIdx()]);
        return;
      }
    }
    if (key.name === "return") {
      submit(value());
      return;
    }
    if (key.name === "backspace" || key.name === "delete") {
      if (value().length > 0) {
        const next = value().slice(0, -1);
        value.set(next);
        error.set(null);
        if (type !== "password")
          updateDropdown(next);
      }
      return;
    }
    if (printable && allowTextInput) {
      const next = value() + key.sequence;
      value.set(next);
      error.set(null);
      if (type !== "password")
        updateDropdown(next);
    }
  }));
  return box(renderer, { flexDirection: "column" }, [
    box(renderer, { marginBottom: 1 }, [promptLine]),
    inputBox,
    dropdownBox,
    errorBox,
    box(renderer, {}, [helpLine])
  ]);
}

export { userInput };
