/**
 * UI wrapper around @clack/prompts.
 *
 * Goal: centralize prompt/styling primitives so commands don't import
 * `@clack/prompts` directly. This keeps the migration from the legacy
 * `prompts` package incremental — commands can adopt this module one
 * by one without touching the rest.
 *
 * Cancel handling: every prompt auto-detects Ctrl+C via `isCancel`
 * and calls the optional `onCancel` callback (or exits with code 0
 * and a friendly message). Callers don't need to wire `onCancel`
 * in each call like they did with the old `prompts` library.
 */

const clack = require('@clack/prompts');
const kleur = require('kleur');

function formatElapsedSeconds(startTime) {
  const elapsed = Math.floor((Date.now() - startTime) / 1000);
  if (elapsed < 60) return `${elapsed}s`;
  const m = Math.floor(elapsed / 60);
  const s = elapsed % 60;
  return `${m}m ${s}s`;
}

function handleCancel(result, onCancel) {
  if (clack.isCancel(result)) {
    if (typeof onCancel === 'function') {
      onCancel();
    } else {
      clack.cancel('Operação cancelada.');
    }
    process.exit(0);
  }
  return result;
}

async function select({ message, options, initialValue, onCancel }) {
  const result = await clack.select({ message, options, initialValue });
  return handleCancel(result, onCancel);
}

async function text({ message, placeholder, initialValue, defaultValue, validate, onCancel }) {
  const result = await clack.text({
    message,
    placeholder,
    initialValue,
    defaultValue,
    validate: validate
      ? (value) => {
          const out = validate(value);
          if (out === true || out == null) return undefined;
          return out;
        }
      : undefined,
  });
  return handleCancel(result, onCancel);
}

async function password({ message, mask, validate, onCancel }) {
  const result = await clack.password({
    message,
    mask,
    validate: validate
      ? (value) => {
          const out = validate(value);
          if (out === true || out == null) return undefined;
          return out;
        }
      : undefined,
  });
  return handleCancel(result, onCancel);
}

async function confirm({ message, initialValue = true, onCancel }) {
  const result = await clack.confirm({ message, initialValue });
  return handleCancel(result, onCancel);
}

async function multiselect({ message, options, initialValues, required = false, onCancel }) {
  const result = await clack.multiselect({ message, options, initialValues, required });
  return handleCancel(result, onCancel);
}

function intro(title) { clack.intro(title); }
function outro(message) { clack.outro(message); }
function note(message, title) { clack.note(message, title); }
function cancel(message) { clack.cancel(message); }

/**
 * Spinner integrated with Clack's vertical line (│).
 * Returns: { start(msg), message(msg), stop(msg, code?) }
 * code: 0 = success (✦), 1 = cancel (■), 2 = error (▲)
 */
function spinner() { return clack.spinner(); }

/**
 * Spinner with an automatic elapsed-time suffix that ticks every second.
 * Same API as spinner() — useful for operations that take >30s so the user
 * sees the clock moving even when the underlying tool emits no progress.
 *
 *   const s = ui.timedSpinner();
 *   s.start('Deploying…');          // "Deploying…  [0s]"
 *   // ... 73 seconds later
 *   s.stop('Deploy done');          // "Deploy done  [1m 13s]"
 */
function timedSpinner() {
  const s = clack.spinner();
  let startTime = null;
  let currentMessage = '';
  let tick = null;

  const render = (msg) => {
    if (!msg) return '';
    if (!startTime) return msg;
    return `${msg}  ${kleur.dim(`[${formatElapsedSeconds(startTime)}]`)}`;
  };

  const stopTick = () => {
    if (tick) { clearInterval(tick); tick = null; }
  };

  return {
    start(msg) {
      startTime = Date.now();
      currentMessage = msg || '';
      s.start(render(currentMessage));
      tick = setInterval(() => {
        if (currentMessage) s.message(render(currentMessage));
      }, 1000);
    },
    message(msg) {
      currentMessage = msg || '';
      s.message(render(currentMessage));
    },
    stop(msg, code) {
      stopTick();
      const finalMsg = msg != null ? render(msg) : render(currentMessage);
      s.stop(finalMsg, code);
    },
    error(msg) {
      stopTick();
      s.error(msg != null ? render(msg) : render(currentMessage));
    },
  };
}

/**
 * Multi-step spinner where every step shows its own elapsed-time counter.
 * Use for long sequential flows like Firebase project creation so the user
 * can tell which steps are slow.
 */
function makeTimedStepper() {
  let current = null;
  let currentMsg = '';
  return {
    next(text) {
      if (current) current.stop(currentMsg);
      current = timedSpinner();
      currentMsg = text;
      current.start(text);
    },
    update(text) {
      if (current) {
        currentMsg = text;
        current.message(text);
      }
    },
    succeed(text) {
      if (current) {
        current.stop(text || currentMsg);
        current = null;
        currentMsg = '';
      }
    },
    fail(text) {
      if (current) {
        current.error(text || currentMsg);
        current = null;
        currentMsg = '';
      }
    },
    warn(text) {
      if (current) {
        current.stop(`⚠ ${text || currentMsg}`);
        current = null;
        currentMsg = '';
      }
    },
    stop() {
      if (current) {
        current.stop(currentMsg);
        current = null;
        currentMsg = '';
      }
    },
  };
}

/**
 * Multi-step spinner: each .next(text) succeeds the previous step
 * with the previous message, then starts a new step with `text`.
 * Mimics the old ora-based makeProgressSpinner() but uses Clack's
 * vertical-line aesthetic so the line stays connected.
 */
function makeStepper() {
  let current = null;
  let currentMsg = '';
  return {
    next(text) {
      if (current) current.stop(currentMsg);
      current = clack.spinner();
      currentMsg = text;
      current.start(text);
    },
    update(text) {
      if (current) {
        currentMsg = text;
        current.message(text);
      }
    },
    succeed(text) {
      if (current) {
        current.stop(text || currentMsg);
        current = null;
        currentMsg = '';
      }
    },
    fail(text) {
      if (current) {
        // .error() renders the red ▲ icon; .stop() defaults to green ✦ success.
        current.error(text || currentMsg);
        current = null;
        currentMsg = '';
      }
    },
    warn(text) {
      // Clack has no "warn" terminal state — prefix with ⚠ and close as success.
      if (current) {
        current.stop(`⚠ ${text || currentMsg}`);
        current = null;
        currentMsg = '';
      }
    },
    stop() {
      if (current) {
        current.stop(currentMsg);
        current = null;
        currentMsg = '';
      }
    },
  };
}

/**
 * Long-running task with rolling log output (e.g. pub get, build_runner).
 * Shows last N lines while running; collapses to a single ✦ on success.
 * Options: { title, limit, retainLog }
 */
function taskLog(options) { return clack.taskLog(options); }

/**
 * Progress bar for operations with known total steps.
 * Returns: { start(msg), advance(n, msg?), message(msg), stop(msg) }
 */
function progress(options) { return clack.progress(options); }

const log = clack.log;

module.exports = {
  select,
  text,
  password,
  confirm,
  multiselect,
  intro,
  outro,
  note,
  cancel,
  spinner,
  timedSpinner,
  makeStepper,
  makeTimedStepper,
  taskLog,
  progress,
  log,
  isCancel: clack.isCancel,
};
