# app/controllers/admin/eventos_notificacao_controller.rb
#
# Cadastro dos eventos e de quais grupos assinam cada um.
#
# O ADM cria quantos eventos quiser, mas o GATILHO sai de EventoNotificacao::
# GATILHOS — gatilho é código. `manual` cobre o aviso pontual (o texto é
# digitado na hora do disparo) e `agendado` cobre o resumo periódico.
class Admin::EventosNotificacaoController < ApplicationController
  before_action :set_evento, only: %i[edit update destroy disparar]
  before_action :carregar_grupos, only: %i[new create edit update]

  def index
    authorize EventoNotificacao
    @eventos = policy_scope(EventoNotificacao)
               .ordenados.includes(assinaturas: :grupo_contato)
  end

  def new
    @evento = EventoNotificacao.new(gatilho: 'manual', ativo: true, hora: 8)
    authorize @evento
    montar_assinaturas
  end

  def create
    @evento = EventoNotificacao.new(evento_params)
    authorize @evento

    if @evento.save
      salvar_assinaturas
      registrar("Criou evento de notificação #{@evento.nome}")
      redirect_to admin_eventos_path, notice: 'Evento criado.'
    else
      montar_assinaturas
      render :new, status: :unprocessable_entity
    end
  end

  def edit
    authorize @evento
    montar_assinaturas
  end

  def update
    authorize @evento

    # `chave` é o que o código usa para achar o evento no disparo. Deixar o ADM
    # renomear a chave de um evento de sistema quebraria o gatilho em silêncio.
    permitidos = @evento.sistema? ? evento_params.except(:chave, :gatilho) : evento_params

    if @evento.update(permitidos)
      salvar_assinaturas
      registrar("Editou evento de notificação #{@evento.nome}")
      redirect_to admin_eventos_path, notice: 'Evento atualizado.'
    else
      montar_assinaturas
      render :edit, status: :unprocessable_entity
    end
  end

  def destroy
    authorize @evento
    nome = @evento.nome
    @evento.destroy
    registrar("Excluiu evento de notificação #{nome}")
    redirect_to admin_eventos_path, notice: 'Evento removido.'
  end

  # Disparo manual: manda mensagem DE VERDADE para todos os grupos assinantes.
  # O texto é digitado na hora — o editor de blocos ainda não existe.
  def disparar
    authorize @evento, :disparar?

    corpo = params[:corpo].to_s.strip
    # Com mensagem montada no editor o texto é opcional: o template vence e o
    # corpo digitado só serve de reserva para o canal que ainda não tem um.
    com_template = MensagemTemplate::CANAIS.any? { |c| @evento.template_utilizavel(c) }
    if corpo.blank? && !com_template
      return redirect_to(admin_eventos_path, alert: 'Escreva a mensagem ou monte o template antes de disparar.')
    end

    Notificacao::Despachante.disparar(chave: @evento.chave,
                                      dados: Notificacao::Variaveis.comuns_reais,
                                      assunto: params[:assunto].presence || @evento.nome,
                                      corpo: corpo)
    registrar("Disparou manualmente o evento #{@evento.nome}")
    redirect_to admin_envios_path,
                notice: 'Disparo agendado. Acompanhe o resultado aqui.'
  end

  private

  def set_evento
    @evento = EventoNotificacao.find(params[:id])
  end

  def carregar_grupos
    @grupos = GrupoContato.ativos.ordenados
  end

  # Uma linha por grupo ativo, marcada quando já existe assinatura. É o que faz
  # a tela mostrar TODOS os grupos com checkbox, e não só os já assinados.
  def montar_assinaturas
    existentes = @evento.assinaturas.index_by(&:grupo_contato_id)
    @linhas_assinatura = @grupos.map do |grupo|
      { grupo: grupo, assinatura: existentes[grupo.id] }
    end
  end

  def salvar_assinaturas
    # ActionController::Parameters, indexado pela id do grupo como STRING (é
    # assim que o form serializa: assinaturas[12][ativo]). Nada de `to_h` aqui:
    # em params não permitidos ele levanta.
    marcadas = params.fetch(:assinaturas, {})

    GrupoContato.ativos.find_each do |grupo|
      dados = marcadas[grupo.id.to_s]
      atual = @evento.assinaturas.find_by(grupo_contato_id: grupo.id)

      if dados.present? && dados[:ativo] == '1'
        canal = GrupoEventoAssinatura::CANAIS.include?(dados[:canal]) ? dados[:canal] : 'ambos'
        atual ? atual.update(canal: canal, ativo: true)
              : @evento.assinaturas.create(grupo_contato: grupo, canal: canal, ativo: true)
      elsif atual
        atual.destroy
      end
    end
  end

  def evento_params
    params.require(:evento_notificacao)
          .permit(:nome, :chave, :gatilho, :descricao, :ativo, :notificar_envolvido,
                  :frequencia, :hora, :dia_semana)
  end

  def registrar(detalhes)
    AuditoriaLog.registrar(user: current_user, acao: 'evento_notificacao',
                           entidade: 'EventoNotificacao',
                           dados_novos: { detalhes: detalhes }, request: request)
  end
end
