# app/services/notificacao/renderizador.rb
#
# Blocos + dados => mensagem pronta. Duas saídas do MESMO template:
#
#   #texto → WhatsApp (texto puro, com o *negrito* do WhatsApp)
#   #html  → e-mail (tabelas com estilo inline, que é o que cliente de e-mail
#            renderiza de forma previsível)
#
# ⚠️ TODO texto vindo do editor e TODO valor de variável passa por escape no
#    caminho HTML. O corpo é digitado numa tela e viraria injeção de HTML no
#    e-mail — e o preview usa este mesmo renderizador, então um XSS aqui
#    atingiria primeiro o próprio admin.
#
# O preview da tela chama exatamente estes métodos: uma segunda implementação em
# JavaScript inevitavelmente divergiria do que é enviado de verdade.
module Notificacao
  class Renderizador
    # Cores fixas: cliente de e-mail não lê CSS externo nem variável de tema.
    LARANJA = '#f97316'.freeze
    ESCURO  = '#111111'.freeze
    CINZA   = '#666666'.freeze

    def initialize(blocos, dados = {})
      @blocos = Array(blocos).select { |b| b.is_a?(Hash) && Blocos.valido?(b['tipo']) }
      @dados  = (dados || {}).transform_keys(&:to_s)
    end

    # ── WhatsApp ────────────────────────────────────────────────
    def texto
      @blocos.filter_map { |bloco| texto_do(bloco) }.join("\n\n").strip
    end

    # ── E-mail ──────────────────────────────────────────────────
    def html
      corpo = @blocos.filter_map { |bloco| html_do(bloco) }.join("\n")
      <<~HTML
        <div style="font-family:Arial,Helvetica,sans-serif;font-size:15px;color:#{ESCURO};line-height:1.6;max-width:600px;">
        #{corpo}
        </div>
      HTML
    end

    # Substitui {{variavel}} pelos dados. Variável sem valor vira string vazia —
    # deixar "{{valor}}" cru numa mensagem enviada é pior do que deixar o espaço.
    def interpolar(texto)
      texto.to_s.gsub(/\{\{\s*(\w+)\s*\}\}/) { @dados[Regexp.last_match(1)].to_s }
    end

    private

    def escapar(valor) = ERB::Util.html_escape(interpolar(valor))

    def texto_do(bloco)
      case bloco['tipo']
      when 'cabecalho'
        [negrito(bloco['titulo']), interpolar(bloco['subtitulo'])].reject(&:blank?).join("\n").presence
      when 'texto', 'rodape'
        interpolar(bloco['texto']).presence
      when 'aviso'
        conteudo = interpolar(bloco['texto'])
        conteudo.present? ? "⚠️ #{conteudo}" : nil
      when 'tabela'
        linhas = Array(bloco['linhas']).filter_map do |linha|
          next unless linha.is_a?(Hash)
          rotulo = interpolar(linha['rotulo'])
          valor  = interpolar(linha['valor'])
          next if rotulo.blank? && valor.blank?
          "• #{rotulo}: #{valor}".strip
        end
        linhas.any? ? linhas.join("\n") : nil
      when 'botao'
        url = interpolar(bloco['url'])
        return nil if url.blank?
        [interpolar(bloco['rotulo']).presence, url].compact.join(': ')
      when 'divisor'
        '——————————'
      end
    end

    def negrito(valor)
      conteudo = interpolar(valor)
      conteudo.present? ? "*#{conteudo}*" : ''
    end

    def html_do(bloco)
      case bloco['tipo']
      when 'cabecalho'  then html_cabecalho(bloco)
      when 'texto'      then html_paragrafo(bloco['texto'])
      when 'aviso'      then html_aviso(bloco)
      when 'tabela'     then html_tabela(bloco)
      when 'botao'      then html_botao(bloco)
      when 'divisor'    then %(<hr style="border:none;border-top:1px solid #e5e5e5;margin:20px 0;">)
      when 'rodape'     then html_rodape(bloco)
      end
    end

    def html_cabecalho(bloco)
      titulo = escapar(bloco['titulo'])
      sub    = escapar(bloco['subtitulo'])
      return nil if titulo.blank? && sub.blank?

      partes = []
      partes << %(<h1 style="margin:0 0 4px;font-size:20px;color:#{ESCURO};">#{titulo}</h1>) if titulo.present?
      partes << %(<p style="margin:0 0 16px;font-size:14px;color:#{CINZA};">#{sub}</p>) if sub.present?
      partes.join("\n")
    end

    # `simple_format` não serve aqui: ele produz HTML sem os estilos inline que o
    # cliente de e-mail precisa. As quebras de linha viram <br>, já escapadas.
    def html_paragrafo(valor)
      conteudo = escapar(valor)
      return nil if conteudo.blank?

      %(<p style="margin:0 0 14px;">#{conteudo.gsub("\n", '<br>')}</p>)
    end

    def html_aviso(bloco)
      conteudo = escapar(bloco['texto'])
      return nil if conteudo.blank?

      %(<div style="margin:0 0 16px;padding:12px 14px;background:#fff8e1;border-left:4px solid #f0ad4e;) +
        %(font-size:14px;color:#5c4400;">#{conteudo.gsub("\n", '<br>')}</div>)
    end

    def html_tabela(bloco)
      linhas = Array(bloco['linhas']).filter_map do |linha|
        next unless linha.is_a?(Hash)
        rotulo = escapar(linha['rotulo'])
        valor  = escapar(linha['valor'])
        next if rotulo.blank? && valor.blank?

        %(<tr><td style="padding:8px 12px;border-bottom:1px solid #eee;color:#{CINZA};">#{rotulo}</td>) +
          %(<td style="padding:8px 12px;border-bottom:1px solid #eee;text-align:right;font-weight:bold;">#{valor}</td></tr>)
      end
      return nil if linhas.empty?

      %(<table role="presentation" cellpadding="0" cellspacing="0" style="width:100%;border-collapse:collapse;margin:0 0 16px;font-size:14px;">) +
        linhas.join + '</table>'
    end

    def html_botao(bloco)
      url = interpolar(bloco['url']).to_s.strip
      # Só http(s): `javascript:` num href montado na tela seria clique armado.
      return nil unless url.match?(%r{\Ahttps?://}i)

      rotulo = escapar(bloco['rotulo']).presence || 'Abrir'
      %(<p style="margin:0 0 18px;"><a href="#{ERB::Util.html_escape(url)}" ) +
        %(style="display:inline-block;padding:11px 20px;background:#{LARANJA};color:#000;) +
        %(text-decoration:none;border-radius:8px;font-weight:bold;font-size:14px;">#{rotulo}</a></p>)
    end

    def html_rodape(bloco)
      conteudo = escapar(bloco['texto'])
      return nil if conteudo.blank?

      %(<p style="margin:24px 0 0;font-size:12px;color:#999;">#{conteudo.gsub("\n", '<br>')}</p>)
    end
  end
end
