// bot/src/index.ts — Ponto de entrada do SDD Slack Bot (Bolt + Socket Mode)

import path from 'path'
import { App } from '@slack/bolt'
import { Config, loadConfig, getSessionPrefix } from './config'
import { createLLMProvider } from './providers/llm/index'
import { SlackProvider } from './providers/slack'
import { JiraProvider } from './providers/jira'
import { BitbucketProvider } from './providers/bitbucket'
import { SessionStore } from './session/store'
import { SessionValidator } from './session/validator'
import { WorktreeManager } from './worktree'
import { VersionTracker } from './versioning'
import { BotCriarCommand } from './commands/bot-criar'
import { CardEditarCommand } from './commands/card-editar'
import { AprovadoCommand } from './commands/aprovado'

async function main(): Promise<void> {
  // ── 1. Configuração ──────────────────────────────────────────────────────────

  const configInstance = Config.getInstance()
  const config = loadConfig()
  const prefix = getSessionPrefix(config)

  // ── 2. Raiz do projeto ───────────────────────────────────────────────────────

  const projectRoot = process.env['SDD_BOT_ROOT']
    ? path.resolve(process.env['SDD_BOT_ROOT'])
    : configInstance.getProjectRoot()

  // ── 3. Bolt App ──────────────────────────────────────────────────────────────

  const app = new App({
    socketMode: true,
    token: config.slack.bot_token,
    appToken: config.slack.app_token,
  })

  // ── 4. Providers e stores ────────────────────────────────────────────────────

  const llm = await createLLMProvider(config.llm)

  const slackProvider = new SlackProvider(config.slack, app)
  const jiraProvider = new JiraProvider(config.jira)
  const bitbucketProvider = new BitbucketProvider(config.bitbucket, projectRoot)

  const sessionsPath = path.join(projectRoot, '.sdd', 'bot', 'sessions.json')
  const sessionStore = SessionStore.getInstance(sessionsPath)
  const sessionValidator = new SessionValidator(sessionStore)

  const worktreeManager = new WorktreeManager(projectRoot)

  const versionTrackerFactory = (cardId: string): VersionTracker =>
    VersionTracker.getInstance(cardId, projectRoot)

  // ── 5. Comandos ──────────────────────────────────────────────────────────────

  const botCriarCommand = new BotCriarCommand(
    jiraProvider,
    worktreeManager,
    slackProvider,
    bitbucketProvider,
    llm,
    sessionStore,
    versionTrackerFactory,
    config,
  )

  const cardEditarCommand = new CardEditarCommand(
    jiraProvider,
    worktreeManager,
    slackProvider,
    sessionStore,
    sessionValidator,
    versionTrackerFactory,
    llm,
    config,
  )

  const aprovadoCommand = new AprovadoCommand(
    jiraProvider,
    worktreeManager,
    slackProvider,
    sessionStore,
    sessionValidator,
    versionTrackerFactory,
    config,
  )

  // ── 6. Helper: postar no canal de logs sem lançar exceção ────────────────────

  const postToLogs = async (text: string): Promise<void> => {
    try {
      const logsChannel = slackProvider.getLogsChannel()
      await slackProvider.postMessage(logsChannel, text)
    } catch {
      // silencia falha de log para não causar loop
    }
  }

  // ── 7. Handler /sdd ──────────────────────────────────────────────────────────

  app.command('/sdd', async ({ command, ack, respond }) => {
    // Bolt exige ack() imediato (< 3 s); trabalho assíncrono ocorre depois
    await ack()

    const channelId = command.channel_id
    const userId = command.user_id
    const text = (command.text ?? '').trim()
    const commandsChannel = slackProvider.getCommandsChannel()

    if (text.startsWith('bot-criar ')) {
      const description = text.slice('bot-criar '.length).trim()

      // Validar canal
      const channelName = command.channel_name ?? ''
      if (channelName !== commandsChannel && channelId !== commandsChannel) {
        await respond(
          `:x: O comando \`/sdd bot-criar\` só pode ser usado no canal *#${commandsChannel}*.`,
        )
        return
      }

      if (!description) {
        await respond(':x: Informe a descrição da feature. Uso: `/sdd bot-criar <descrição>`')
        return
      }

      try {
        await botCriarCommand.execute(userId, description, async (msg) => {
          await respond(msg)
        })
      } catch (err: unknown) {
        const msg = err instanceof Error ? err.message : String(err)
        const errorText = `:x: Erro ao executar *bot-criar*: ${msg}`
        await respond(errorText).catch(() => undefined)
        await postToLogs(`[bot-criar] Erro — usuário <@${userId}>: ${msg}`)
      }
    } else if (text.startsWith('card-editar ')) {
      const newText = text.slice('card-editar '.length).trim()

      // Validar canal: deve ser uma sessão rastreada
      const session = sessionStore.findByChannelId(channelId)
      if (!session) {
        await respond(
          ':x: O comando `/sdd card-editar` só pode ser usado em um canal de sessão ativo.',
        )
        return
      }

      // Validar isolamento de sessão
      try {
        sessionValidator.validate(channelId, session.card_id)
      } catch (err: unknown) {
        const msg = err instanceof Error ? err.message : String(err)
        await respond(`:x: Falha de isolamento de sessão: ${msg}`)
        await postToLogs(`[card-editar] Mismatch de sessão — canal ${channelId}: ${msg}`)
        return
      }

      if (!newText) {
        await respond(':x: Informe o novo texto. Uso: `/sdd card-editar <texto>`')
        return
      }

      try {
        await cardEditarCommand.execute(channelId, userId, newText)
      } catch (err: unknown) {
        const msg = err instanceof Error ? err.message : String(err)
        const errorText = `:x: Erro ao executar *card-editar*: ${msg}`
        try {
          await slackProvider.postMessage(channelId, errorText)
        } catch {
          await respond(errorText).catch(() => undefined)
        }
        await postToLogs(`[card-editar] Erro — canal ${channelId}, usuário <@${userId}>: ${msg}`)
      }
    } else {
      // Subcomando desconhecido — exibir ajuda
      await respond(
        [
          '*Comandos disponíveis do SDD Bot:*',
          `• \`/sdd bot-criar <descrição>\` — cria card, worktree e canal de sessão (use em #${commandsChannel})`,
          '• `/sdd card-editar <texto>` — reavalia artefatos com novo contexto (use no canal da sessão)',
          '',
          'Para aprovar um card, envie `/aprovado` no canal da sessão.',
        ].join('\n'),
      )
    }
  })

  // ── 8. Handler /aprovado ─────────────────────────────────────────────────────

  app.message(/^\/aprovado\s*$/, async ({ message, say }) => {
    // Ack implícito em handlers de mensagem; work começa imediatamente
    const msg = message as { channel?: string; user?: string }
    const channelId = msg.channel ?? ''
    const userId = msg.user ?? ''

    // Validar canal rastreado
    const session = sessionStore.findByChannelId(channelId)
    if (!session) {
      // Ignorar silenciosamente — /aprovado em canal não rastreado não é um erro
      return
    }

    // Validar isolamento de sessão
    try {
      sessionValidator.validate(channelId, session.card_id)
    } catch (err: unknown) {
      const errMsg = err instanceof Error ? err.message : String(err)
      await say(`:x: Falha de isolamento de sessão: ${errMsg}`).catch(() => undefined)
      await postToLogs(`[aprovado] Mismatch de sessão — canal ${channelId}: ${errMsg}`)
      return
    }

    try {
      await aprovadoCommand.execute(channelId, userId)
    } catch (err: unknown) {
      const errMsg = err instanceof Error ? err.message : String(err)
      const errorText = `:x: Erro ao executar *aprovado*: ${errMsg}`
      await say(errorText).catch(() => undefined)
      await postToLogs(`[aprovado] Erro — canal ${channelId}, usuário <@${userId}>: ${errMsg}`)
    }
  })

  // ── 9. Handler de erros global ───────────────────────────────────────────────

  app.error(async (error) => {
    console.error('[sdd-bot] Erro não tratado pelo Bolt:', error)
    await postToLogs(`:rotating_light: Erro global do bot: ${error.message}`)
  })

  // ── 10. Start ────────────────────────────────────────────────────────────────

  await app.start()
  console.log(`Bot SDD iniciado · Sessão: ${config.slack.session_name}`)
}

main().catch((err: unknown) => {
  const msg = err instanceof Error ? err.message : String(err)
  console.error(`[sdd-bot] Falha fatal na inicialização: ${msg}`)
  process.exit(1)
})
