# app/models/concerns/atributo_cifrado.rb
#
# Cifra atributos sensíveis (senha SMTP, token do Twilio) em colunas
# `<nome>_cifrado` usando ActiveSupport::MessageEncryptor (AES-256-GCM).
#
# POR QUE NÃO ActiveRecord Encryption (`encrypts :campo`): este projeto não usa
# `config/credentials.yml.enc` nem `config/master.key` — tudo vem do .env. Ligar
# o AR Encryption exigiria configurar 3 chaves novas e depender da ordem de
# execução dos initializers do framework. Aqui a chave é DERIVADA do
# secret_key_base, sem nada novo para o operador gerar.
#
# ⚠️ ROTAÇÃO: se o SECRET_KEY_BASE do servidor for trocado, os valores já
#    gravados viram ilegíveis. O reader devolve nil (nunca estoura 500), o app
#    cai no fallback do .env e a tela mostra um aviso pedindo para redigitar.
#    Para ficar imune a isso, defina NOTIFICACAO_SECRET no .env (qualquer string
#    longa e fixa) — a derivação passa a usar ela em vez do secret_key_base.
module AtributoCifrado
  extend ActiveSupport::Concern

  SALT = 'reem-notas/notificacao/v1'

  class_methods do
    def atributo_cifrado(*nomes)
      nomes.each do |nome|
        coluna = :"#{nome}_cifrado"

        define_method(nome) { AtributoCifrado.decifrar(self[coluna]) }

        define_method(:"#{nome}=") do |valor|
          texto = valor.to_s
          # Sem isso, salvar a tela sem mexer no campo geraria um criptograma
          # novo (o IV é aleatório) e o dirty tracking acusaria mudança à toa.
          next if texto == send(nome)

          self[coluna] = texto.blank? ? nil : AtributoCifrado.cifrar(texto)
        end

        define_method(:"#{nome}?") { send(nome).present? }
      end
    end
  end

  def self.cifrar(texto)
    cofre.encrypt_and_sign(texto)
  end

  def self.decifrar(blob)
    return nil if blob.blank?

    cofre.decrypt_and_verify(blob)
  rescue ActiveSupport::MessageEncryptor::InvalidMessage,
         ActiveSupport::MessageVerifier::InvalidSignature => e
    Rails.logger.error("[AtributoCifrado] valor ilegível (SECRET_KEY_BASE mudou?): #{e.class}")
    nil
  end

  # O default do MessageEncryptor no Rails 7.1 é aes-256-gcm → chave de 32 bytes.
  def self.cofre
    @cofre ||= ActiveSupport::MessageEncryptor.new(chave)
  end

  def self.chave
    if (segredo = ENV['NOTIFICACAO_SECRET'].presence)
      ActiveSupport::KeyGenerator
        .new(segredo, hash_digest_class: OpenSSL::Digest::SHA256)
        .generate_key(SALT, 32)
    else
      Rails.application.key_generator.generate_key(SALT, 32)
    end
  end
end
