# app/models/mensagem_template.rb
#
# Corpo de um evento em UM canal, montado com blocos no editor.
#
# `blocos` vem de um campo hidden preenchido por JavaScript — ou seja, de fora.
# Por isso #normalizar_blocos DESCARTA o que não está no catálogo: tipo
# desconhecido, campo que aquele tipo não tem, e qualquer coisa que não seja
# Hash. Sem isso, um JSON adulterado no form viraria conteúdo renderizado sem
# passar por validação nenhuma.
class MensagemTemplate < ApplicationRecord
  CANAIS = %w[whatsapp email].freeze
  # Teto de segurança: um template com milhares de blocos só serviria para
  # travar o render do e-mail.
  MAX_BLOCOS = 40
  MAX_LINHAS_TABELA = 20

  belongs_to :evento_notificacao

  validates :canal, inclusion: { in: CANAIS }
  validates :canal, uniqueness: { scope: :evento_notificacao_id }
  validate  :assunto_no_email

  before_validation :normalizar_blocos

  scope :ativos, -> { where(ativo: true) }
  scope :do_canal, ->(canal) { where(canal: canal.to_s) }

  def email?    = canal == 'email'
  def whatsapp? = canal == 'whatsapp'

  # Um template só entra em uso quando tem conteúdo — template ativo e vazio
  # mandaria mensagem em branco, o que é pior do que cair no texto padrão.
  def utilizavel?
    ativo? && blocos.present?
  end

  def renderizador(dados = {})
    Notificacao::Renderizador.new(blocos, dados)
  end

  def preview(dados = nil)
    dados ||= Notificacao::CatalogoVariaveis.amostra(evento_notificacao&.gatilho)
    r = renderizador(dados)
    email? ? r.html : r.texto
  end

  private

  def normalizar_blocos
    lista = blocos.is_a?(Array) ? blocos : []
    self.blocos = lista.filter_map { |bruto| limpar(bruto) }.first(MAX_BLOCOS)
  end

  def limpar(bruto)
    return nil unless bruto.is_a?(Hash)

    tipo = bruto['tipo'] || bruto[:tipo]
    return nil unless Notificacao::Blocos.valido?(tipo)

    limpo = { 'tipo' => tipo.to_s }
    Notificacao::Blocos.campos(tipo).each do |campo, especie|
      valor = bruto[campo] || bruto[campo.to_sym]
      limpo[campo] = especie == :lista ? limpar_linhas(valor) : valor.to_s
    end
    limpo
  end

  def limpar_linhas(valor)
    Array(valor).filter_map do |linha|
      next unless linha.is_a?(Hash)

      rotulo = (linha['rotulo'] || linha[:rotulo]).to_s
      conteudo = (linha['valor'] || linha[:valor]).to_s
      next if rotulo.blank? && conteudo.blank?

      { 'rotulo' => rotulo, 'valor' => conteudo }
    end.first(MAX_LINHAS_TABELA)
  end

  def assunto_no_email
    return unless email? && ativo? && blocos.present? && assunto.to_s.strip.blank?

    errors.add(:assunto, 'é obrigatório no e-mail')
  end
end
