import type {
  WizardPrompter,
  SelectOption,
  MultiSelectOption,
  ProgressHandle,
} from './interface.js';

export type AnswerValue = string | boolean | string[];

export class HeadlessPrompter implements WizardPrompter {
  private answers: Map<string, AnswerValue>;
  private logs: string[] = [];

  constructor(answers: Record<string, AnswerValue> = {}) {
    this.answers = new Map(Object.entries(answers));
  }

  intro(message: string): void {
    this.logs.push(`[intro] ${message}`);
  }
  outro(message: string): void {
    this.logs.push(`[outro] ${message}`);
  }
  note(message: string, title?: string): void {
    this.logs.push(`[note] ${title ?? ''}: ${message}`);
  }
  log(message: string): void {
    this.logs.push(`[log] ${message}`);
  }
  warn(message: string): void {
    this.logs.push(`[warn] ${message}`);
  }

  async text(opts: {
    message: string;
    placeholder?: string;
    defaultValue?: string;
    initialValue?: string;
    validate?: (value: string) => string | void;
  }): Promise<string> {
    const answer = this.answers.get(opts.message);
    const value =
      typeof answer === 'string'
        ? answer
        : opts.initialValue !== undefined
          ? opts.initialValue
          : opts.defaultValue !== undefined
            ? opts.defaultValue
            : undefined;

    if (value === undefined) {
      throw new Error(`HeadlessPrompter: no answer for "${opts.message}"`);
    }

    if (opts.validate) {
      const error = opts.validate(value);
      if (error)
        throw new Error(`HeadlessPrompter validation failed for "${opts.message}": ${error}`);
    }

    return value;
  }

  async confirm(opts: { message: string; initialValue?: boolean }): Promise<boolean> {
    const answer = this.answers.get(opts.message);
    if (typeof answer === 'boolean') return answer;
    return opts.initialValue ?? true;
  }

  async select<T>(opts: {
    message: string;
    options: SelectOption<T>[];
    initialValue?: T;
  }): Promise<T> {
    const answer = this.answers.get(opts.message);
    if (answer !== undefined) {
      // Find matching option by value string comparison
      const match = opts.options.find((o) => String(o.value) === String(answer));
      if (match) return match.value;
    }
    if (opts.initialValue !== undefined) return opts.initialValue;
    if (opts.options.length === 0) {
      throw new Error(`HeadlessPrompter: no options for "${opts.message}"`);
    }
    const first = opts.options[0];
    if (first === undefined) {
      throw new Error(`HeadlessPrompter: no options for "${opts.message}"`);
    }
    return first.value;
  }

  async multiselect<T>(opts: {
    message: string;
    options: MultiSelectOption<T>[];
    required?: boolean;
  }): Promise<T[]> {
    const answer = this.answers.get(opts.message);
    if (Array.isArray(answer)) {
      return opts.options
        .filter((o) => (answer as string[]).includes(String(o.value)))
        .map((o) => o.value);
    }
    return opts.options.filter((o) => o.selected).map((o) => o.value);
  }

  async groupMultiselect<T>(opts: {
    message: string;
    options: Record<string, MultiSelectOption<T>[]>;
    required?: boolean;
  }): Promise<T[]> {
    const answer = this.answers.get(opts.message);
    if (Array.isArray(answer)) {
      const all = Object.values(opts.options).flat();
      return all.filter((o) => (answer as string[]).includes(String(o.value))).map((o) => o.value);
    }
    return Object.values(opts.options)
      .flat()
      .filter((o) => o.selected)
      .map((o) => o.value);
  }

  spinner(): ProgressHandle {
    return {
      update(_message: string) {},
      stop(_message?: string) {},
    };
  }

  separator(): void {}

  getLogs(): string[] {
    return [...this.logs];
  }
}
