require 'rails_helper'

RSpec.describe Notificacao::Despachante do
  let(:evento)    { create(:evento_notificacao) }
  let(:diretoria) { create(:grupo_contato) }
  let(:config)    { ConfiguracaoNotificacao.instancia }

  before do
    config.update_columns(whatsapp_ativo: true, whatsapp_provedor: 'baileys',
                          email_notificacoes_ativo: true, whatsapp_intervalo_segundos: 0)
    # Nunca tocar a ponte de verdade no teste.
    allow(Notificacao::Whatsapp).to receive(:enviar)
      .and_return(Notificacao::ClienteWhatsapp::Resposta.new(ok: true))
    allow(ConfiguracaoNotificacao).to receive(:instancia).and_return(config)
  end

  def despachar(envolvido: nil, dados: {})
    described_class.new(evento.reload, dados: dados, assunto: 'Assunto',
                        corpo: 'Corpo', envolvido: envolvido).executar
  end

  it 'envia para os contatos dos grupos assinantes e registra cada envio' do
    contato = create(:contato, grupo_contato: diretoria, telefone: '11 92005-1157', email: 'dir@reem.com')
    create(:grupo_evento_assinatura, grupo_contato: diretoria, evento_notificacao: evento)

    expect { despachar }.to change(NotificacaoEnvio, :count).by(2) # whatsapp + email

    expect(Notificacao::Whatsapp).to have_received(:enviar)
      .with(hash_including(para: contato.telefone))
    expect(NotificacaoEnvio.where(canal: 'whatsapp').first).to be_enviado
  end

  # A mesma pessoa pode ser contato de um grupo E a envolvida no fato — sem
  # dedup ela receberia a mensagem duas vezes.
  it 'não manda duas vezes para o mesmo número' do
    create(:contato, grupo_contato: diretoria, telefone: '11 92005-1157', email: nil)
    create(:grupo_evento_assinatura, grupo_contato: diretoria, evento_notificacao: evento, canal: 'whatsapp')
    motorista = create(:motorista, telefone: '(11) 92005-1157')

    despachar(envolvido: motorista)

    expect(NotificacaoEnvio.where(canal: 'whatsapp').count).to eq(1)
  end

  it 'inclui o envolvido que não é contato de nenhum grupo' do
    create(:grupo_evento_assinatura, grupo_contato: diretoria, evento_notificacao: evento)
    motorista = create(:motorista, telefone: '(11) 98888-7777')

    despachar(envolvido: motorista)

    expect(NotificacaoEnvio.where(canal: 'whatsapp').pluck(:destino)).to eq(['+5511988887777'])
  end

  it 'não avisa o envolvido quando o evento diz para não avisar' do
    evento.update!(notificar_envolvido: false)
    motorista = create(:motorista, telefone: '(11) 98888-7777')

    despachar(envolvido: motorista)

    expect(NotificacaoEnvio.where(canal: 'whatsapp')).to be_empty
  end

  it 'registra a falha em vez de levantar — envio não pode travar um fechamento' do
    create(:contato, grupo_contato: diretoria, telefone: '11 92005-1157', email: nil)
    create(:grupo_evento_assinatura, grupo_contato: diretoria, evento_notificacao: evento, canal: 'whatsapp')
    allow(Notificacao::Whatsapp).to receive(:enviar)
      .and_return(Notificacao::ClienteWhatsapp::Resposta.new(ok: false, erro: 'whatsapp desconectado'))

    expect { despachar }.not_to raise_error

    envio = NotificacaoEnvio.last
    expect(envio).to be_falhou
    expect(envio.erro).to include('desconectado')
  end

  # O editor de blocos vence o texto padrão — e o fallback existe para que ligar
  # o editor não apague as notificações dos eventos que já funcionavam.
  describe 'template de blocos' do
    before do
      create(:contato, grupo_contato: diretoria, telefone: '11 92005-1157', email: 'dir@reem.com')
      create(:grupo_evento_assinatura, grupo_contato: diretoria, evento_notificacao: evento)
    end

    it 'sem template, usa o corpo passado pelo chamador' do
      despachar
      expect(NotificacaoEnvio.find_by(canal: 'whatsapp').corpo).to eq('Corpo')
    end

    it 'com template, o corpo sai renderizado por canal' do
      MensagemTemplate.create!(evento_notificacao: evento, canal: 'whatsapp', ativo: true,
                               blocos: [{ 'tipo' => 'texto', 'texto' => 'Oi {{contato}}, {{valor}}' }])
      MensagemTemplate.create!(evento_notificacao: evento, canal: 'email', ativo: true,
                               assunto: 'Fechamento de {{contato}}',
                               blocos: [{ 'tipo' => 'texto', 'texto' => 'Corpo do e-mail' }])

      despachar(dados: { 'valor' => 'R$ 10,00' })

      zap = NotificacaoEnvio.find_by(canal: 'whatsapp')
      expect(zap.corpo).to include('R$ 10,00')

      email = NotificacaoEnvio.find_by(canal: 'email')
      expect(email.corpo).to include('<p')            # HTML montado
      expect(email.assunto).to include('Fechamento')
    end

    # {{contato}} é o primeiro nome de QUEM RECEBE: um render compartilhado
    # mandaria o nome da primeira pessoa para todo mundo.
    it 'renderiza {{contato}} por destinatário' do
      create(:contato, grupo_contato: diretoria, nome: 'Ana Paula', telefone: '11 93005-1157', email: nil)
      MensagemTemplate.create!(evento_notificacao: evento, canal: 'whatsapp', ativo: true,
                               blocos: [{ 'tipo' => 'texto', 'texto' => 'Olá {{contato}}' }])

      despachar

      corpos = NotificacaoEnvio.where(canal: 'whatsapp').pluck(:corpo)
      expect(corpos).to include(a_string_including('Ana'))
      expect(corpos.uniq.size).to eq(corpos.size)
    end

    it 'template ativo mas vazio não substitui o texto padrão' do
      MensagemTemplate.create!(evento_notificacao: evento, canal: 'whatsapp', ativo: true, blocos: [])
      despachar
      expect(NotificacaoEnvio.find_by(canal: 'whatsapp').corpo).to eq('Corpo')
    end
  end

  it 'não envia nada quando o canal está desligado na configuração' do
    create(:contato, grupo_contato: diretoria, telefone: '11 92005-1157', email: 'dir@reem.com')
    create(:grupo_evento_assinatura, grupo_contato: diretoria, evento_notificacao: evento)
    config.update_columns(whatsapp_ativo: false, email_notificacoes_ativo: false)

    expect { despachar }.not_to change(NotificacaoEnvio, :count)
  end
end

# Buscar o evento pela `chave` fazia só o de SISTEMA disparar: um evento criado
# pelo ADM com o mesmo gatilho ficava mudo para sempre, sem erro nenhum — a tela
# oferecia o gatilho e nada acontecia.
RSpec.describe Notificacao::Despachante, '.disparar_gatilho' do
  it 'agenda TODOS os eventos ativos do gatilho, não só o de sistema' do
    sistema = create(:evento_notificacao, gatilho: 'valor_alterado', chave: 'valor_alterado', sistema: true)
    do_adm  = create(:evento_notificacao, gatilho: 'valor_alterado', chave: 'aviso_extra')
    create(:evento_notificacao, gatilho: 'pagamento_efetuado', chave: 'outro')

    agendados = []
    allow(NotificacaoJob).to receive(:perform_later) { |id, *| agendados << id }

    described_class.disparar_gatilho(gatilho: 'valor_alterado', corpo: 'x')

    expect(agendados).to contain_exactly(sistema.id, do_adm.id)
  end

  it 'ignora evento desativado' do
    create(:evento_notificacao, gatilho: 'valor_alterado', chave: 'desligado', ativo: false)
    allow(NotificacaoJob).to receive(:perform_later)

    described_class.disparar_gatilho(gatilho: 'valor_alterado', corpo: 'x')

    expect(NotificacaoJob).not_to have_received(:perform_later)
  end

  # O botão "disparar agora" mira UM evento — ali a chave é o identificador certo.
  it '.disparar continua agendando só o evento da chave' do
    alvo = create(:evento_notificacao, gatilho: 'manual', chave: 'so_este')
    create(:evento_notificacao, gatilho: 'manual', chave: 'outro_manual')

    agendados = []
    allow(NotificacaoJob).to receive(:perform_later) { |id, *| agendados << id }

    described_class.disparar(chave: 'so_este', corpo: 'x')

    expect(agendados).to eq([alvo.id])
  end
end
