/**
 * Interactive terminal prompts for the madarX CLI wizard.
 *
 * Zero dependencies — node builtins + the brand stylers in ./banner.js only.
 * The headline piece is `select()`: an arrow-key menu (↑/↓ or j/k, number keys,
 * Enter to choose) in the Sovereign-Mono palette with a single Mars cursor.
 *
 * Degrades safely: with no TTY it returns the default option without drawing.
 */

import { emitKeypressEvents } from 'node:readline';
import { _palette as P, fg, bold, dim } from './banner.js';

const isTTY = () => Boolean(process.stdin.isTTY && process.stdout.isTTY);
const write = (s) => process.stdout.write(s);
const hideCursor = () => write('\x1b[?25l');
const showCursor = () => write('\x1b[?25h');

/**
 * Render one menu option line.
 * @param {{label:string, hint?:string, tag?:string}} opt
 * @param {boolean} active
 */
function optionLine(opt, active) {
  const cursor = active ? fg(P.mars, '→ ') : '  ';
  const label = active ? bold(fg(P.mars, opt.label)) : fg(P.bone, opt.label);
  const tag = opt.tag ? ' ' + fg(opt.tag === 'recommended' ? P.platinum : P.muted, `(${opt.tag})`) : '';
  const hint = opt.hint ? '  ' + dim(fg(P.muted, opt.hint)) : '';
  return `  ${cursor}${label}${tag}${hint}`;
}

/**
 * Arrow-key single-select menu.
 *
 * @param {string} title    bold Mars header (e.g. "Select default model")
 * @param {Array<{label:string, value:any, hint?:string, tag?:string}>} options
 * @param {object} [opts]
 * @param {number} [opts.default=0]  initially highlighted index
 * @param {string} [opts.help]       a dim help line under the title
 * @returns {Promise<any>} the chosen option's `value`
 */
export function select(title, options, opts = {}) {
  const def = Math.max(0, Math.min(opts.default ?? 0, options.length - 1));
  if (!isTTY() || options.length === 0) {
    return Promise.resolve(options[def]?.value);
  }

  return new Promise((resolve) => {
    let idx = def;
    const headerLines = [
      fg(P.mars, bold(title)),
      ...(opts.help ? [dim(fg(P.muted, opts.help))] : []),
      '',
    ];
    const total = headerLines.length + options.length + 1; // +1 footer

    const footer = dim(fg(P.muted, '↑/↓ move · enter select · ⌃C cancel'));

    const draw = (first) => {
      if (!first) write(`\x1b[${total}A`); // jump to top of the block
      const lines = [
        ...headerLines,
        ...options.map((o, i) => optionLine(o, i === idx)),
        footer,
      ];
      write('\r' + lines.map((l) => '\x1b[2K' + l).join('\n') + '\n');
    };

    emitKeypressEvents(process.stdin);
    const wasRaw = process.stdin.isRaw;
    process.stdin.setRawMode(true);
    process.stdin.resume();
    hideCursor();
    draw(true);

    const cleanup = () => {
      process.stdin.removeListener('keypress', onKey);
      process.stdin.setRawMode(wasRaw);
      process.stdin.pause();
      showCursor();
    };

    const onKey = (str, key = {}) => {
      if (key.name === 'up' || key.name === 'k' || (key.name === 'p' && key.ctrl)) {
        idx = (idx - 1 + options.length) % options.length; draw(false);
      } else if (key.name === 'down' || key.name === 'j' || (key.name === 'n' && key.ctrl)) {
        idx = (idx + 1) % options.length; draw(false);
      } else if (str && /^[1-9]$/.test(str) && Number(str) <= options.length) {
        idx = Number(str) - 1; draw(false);
      } else if (key.name === 'return' || key.name === 'enter') {
        cleanup(); resolve(options[idx].value);
      } else if (key.name === 'escape') {
        cleanup(); resolve(options[def].value);
      } else if (key.ctrl && key.name === 'c') {
        cleanup(); write('\n'); process.exit(130);
      }
    };

    process.stdin.on('keypress', onKey);
  });
}

/** Yes/No as a two-item arrow menu (returns boolean). */
export async function confirm(title, { default: dflt = true, help } = {}) {
  const value = await select(title, [
    { label: 'Yes', value: true },
    { label: 'No', value: false },
  ], { default: dflt ? 0 : 1, help });
  return value;
}
