// bot/src/progress.ts — Feedback de progresso contínuo para o usuário.
//
// Dois cenários:
//  - CommandProgress: usa o response_url do slash command (respond) para manter
//    UMA mensagem no canal de comandos e ATUALIZÁ-LA a cada passo, evitando que o
//    usuário fique sem informação entre o envio do comando e a criação do canal.
//  - ChannelProgress: usa chat.postMessage + chat.update num canal específico
//    (ex.: o canal privado do card) para o mesmo efeito de checklist vivo.
//
// Puro TypeScript, sem dependência de SO — funciona em Mac, Linux e Windows.

export type RespondPayload =
  | string
  | {
      text: string
      replace_original?: boolean
      response_type?: 'ephemeral' | 'in_channel'
    }

export type RespondFn = (payload: RespondPayload) => Promise<void>

interface Step {
  label: string
  state: 'doing' | 'done' | 'error'
}

const ICON: Record<Step['state'], string> = {
  doing: '🔄',
  done: '✅',
  error: '❌',
}

/**
 * Checklist vivo enviado pelo response_url de um slash command.
 * Cada passo concluído vira ✅ e o passo em andamento aparece como 🔄.
 */
export class CommandProgress {
  private readonly steps: Step[] = []
  private posted = false

  constructor(
    private readonly respond: RespondFn,
    private readonly header: string,
  ) {}

  /** Posta o checklist inicial com o primeiro passo em andamento. */
  async begin(firstStep: string): Promise<void> {
    this.steps.push({ label: firstStep, state: 'doing' })
    await this.flush()
  }

  /** Conclui o passo atual e inicia o próximo, atualizando a mesma mensagem. */
  async next(nextStep: string): Promise<void> {
    this.markLast('done')
    this.steps.push({ label: nextStep, state: 'doing' })
    await this.flush()
  }

  /** Conclui o último passo e encerra, com uma nota explicativa opcional. */
  async finish(note?: string): Promise<void> {
    this.markLast('done')
    await this.flush(note)
  }

  /** Marca o passo atual como falho, preservando o histórico já mostrado. */
  async fail(reason: string): Promise<void> {
    this.markLast('error')
    await this.flush(`❌ ${reason}`)
  }

  private markLast(state: Step['state']): void {
    if (this.steps.length > 0) this.steps[this.steps.length - 1].state = state
  }

  private render(note?: string): string {
    const lines = [this.header, ...this.steps.map((s) => `${ICON[s.state]} ${s.label}`)]
    if (note) lines.push('', note)
    return lines.join('\n')
  }

  private async flush(note?: string): Promise<void> {
    const payload: RespondPayload = {
      text: this.render(note),
      response_type: 'ephemeral',
      ...(this.posted ? { replace_original: true } : {}),
    }
    await this.respond(payload)
    this.posted = true
  }
}

/** Função compatível com SlackProvider.postMessage / updateMessage. */
export interface ChannelMessenger {
  postMessage(channelId: string, text: string): Promise<string>
  updateMessage(channelId: string, ts: string, text: string): Promise<void>
}

/**
 * Checklist vivo dentro de um canal (ex.: o canal privado do card).
 * Mantém uma única mensagem e a atualiza a cada passo.
 */
export class ChannelProgress {
  private readonly steps: Step[] = []
  private ts: string | null = null

  constructor(
    private readonly slack: ChannelMessenger,
    private readonly channelId: string,
    private readonly header: string,
  ) {}

  async begin(firstStep: string): Promise<void> {
    this.steps.push({ label: firstStep, state: 'doing' })
    await this.flush()
  }

  async next(nextStep: string): Promise<void> {
    this.markLast('done')
    this.steps.push({ label: nextStep, state: 'doing' })
    await this.flush()
  }

  async finish(note?: string): Promise<void> {
    this.markLast('done')
    await this.flush(note)
  }

  async fail(reason: string): Promise<void> {
    this.markLast('error')
    await this.flush(`❌ ${reason}`)
  }

  private markLast(state: Step['state']): void {
    if (this.steps.length > 0) this.steps[this.steps.length - 1].state = state
  }

  private render(note?: string): string {
    const lines = [this.header, ...this.steps.map((s) => `${ICON[s.state]} ${s.label}`)]
    if (note) lines.push('', note)
    return lines.join('\n')
  }

  private async flush(note?: string): Promise<void> {
    const text = this.render(note)
    if (this.ts) {
      await this.slack.updateMessage(this.channelId, this.ts, text)
    } else {
      this.ts = await this.slack.postMessage(this.channelId, text)
    }
  }
}
