// bot/src/commands/aprovado.ts — Aprova o card, promove artefatos e arquiva o canal

import * as path from 'node:path'

import type { BotConfig } from '../types'
import { JiraProvider } from '../providers/jira'
import { SlackProvider } from '../providers/slack'
import { SessionStore } from '../session/store'
import { SessionValidator } from '../session/validator'
import { WorktreeManager } from '../worktree'
import { VersionTracker } from '../versioning'
import { FileHeaderManager } from '../fileHeaders'
import { ChannelProgress } from '../progress'

function now(): string {
  return new Date().toISOString()
}

export class AprovadoCommand {
  constructor(
    private readonly jira: JiraProvider,
    private readonly worktree: WorktreeManager,
    private readonly slack: SlackProvider,
    private readonly sessionStore: SessionStore,
    private readonly sessionValidator: SessionValidator,
    private readonly versionTrackerFactory: (cardId: string) => VersionTracker,
    private readonly config: BotConfig,
  ) {}

  async execute(channelId: string, userId: string): Promise<void> {
    // 1. Resolve session
    const rawEntry = this.sessionStore.findByChannelId(channelId)
    if (!rawEntry) {
      throw new Error(`Nenhuma sessão ativa encontrada para o canal "${channelId}".`)
    }
    const entry = this.sessionValidator.validate(channelId, rawEntry.card_id)

    // 2. Validate ownership
    const adminIds = await this.slack.getAdminUserIds()
    const isAuthorized = this.sessionValidator.validateOwner(userId, entry, adminIds)
    if (!isAuthorized) {
      throw new Error(
        `Usuário <@${userId}> não é o dono do card nem administrador. Apenas o criador ou admins podem aprovar.`,
      )
    }

    // 3-4. Resolve paths
    const cardId = entry.card_id
    const worktreePath = entry.worktree_path
    const docsPath = this.worktree.getDocsPath(cardId)

    // 5. Resolve idea.md path
    const ideaMdPath = path.join(docsPath, 'idea.md')
    const riskMdPath = path.join(docsPath, 'risk-classification.md')

    // Checklist vivo no canal (consultável depois, pois o canal é arquivado).
    const progress = new ChannelProgress(
      this.slack,
      channelId,
      `🟢 *Aprovando o card ${cardId}:*`,
    )

    try {
      // 6. Update status to /aprovado
      await progress.begin('Marcando a ideia como aprovada (Status: /aprovado)...')
      FileHeaderManager.updateStatus(ideaMdPath, '/aprovado')

      // 7-9. Commit and push with approval marker
      await progress.next('Versionando a aprovação no repositório (commit + push)...')
      const versioning = this.versionTrackerFactory(cardId)
      const version = versioning.getNext()
      const versionNumber = Number(version.split('.')[2])
      const commitText = await this.worktree.getCommitText(cardId, 'idea', versionNumber)

      const execa = (await import('execa')).execa

      await execa('git', ['add', '.'], { cwd: worktreePath })
      await execa('git', ['commit', '-m', `${commitText} — aprovado`], { cwd: worktreePath })
      await execa('git', ['push', 'origin', cardId], { cwd: worktreePath })

      // 10. Add Jira comment
      await progress.next('Registrando /aprovado no card do Jira...')
      await this.jira.addComment(cardId, '/aprovado')

      // 11. Log to admin channel
      await progress.next('Registrando a aprovação no canal de logs dos admins...')
      await this.slack.logToAdminChannel(
        `Card ${cardId} aprovado por <@${userId}> em ${now()}`,
        [
          { path: ideaMdPath, title: 'idea.md' },
          { path: riskMdPath, title: 'risk-classification.md' },
        ],
      )

      // 12-13. Notify + archive
      await progress.finish(
        'Card aprovado! As próximas etapas estão liberadas. Este canal será arquivado e ficará disponível só para consulta.',
      )
      await this.slack.archiveChannel(channelId)

      // 14. Remove session
      this.sessionStore.remove(cardId)
    } catch (err: unknown) {
      const msg = err instanceof Error ? err.message : String(err)
      await progress.fail(msg).catch(() => undefined)
      throw err
    }
  }
}
