require 'rails_helper'

RSpec.describe ConfiguracaoNotificacao do
  # Preenchimento mínimo para os toggles passarem nas validações condicionais.
  def config_smtp(extras = {})
    described_class.new({
      smtp_ativo: true, smtp_address: 'smtp.gmail.com', smtp_port: 587,
      smtp_username: 'sistema@reem.com.br', smtp_password: 'segredo123'
    }.merge(extras))
  end

  def config_whats(extras = {})
    described_class.new({
      whatsapp_ativo: true, twilio_account_sid: 'AC123',
      twilio_auth_token: 'tok123', twilio_from: '+14155238886'
    }.merge(extras))
  end

  describe '.instancia' do
    it 'devolve sempre a mesma linha' do
      primeira = described_class.instancia
      expect(described_class.instancia.id).to eq(primeira.id)
      expect(described_class.count).to eq(1)
    end
  end

  describe '.normalizar_telefone' do
    {
      '11 920051157'            => '+5511920051157',
      '(11) 92005-1157'         => '+5511920051157',
      '011 92005-1157'          => '+5511920051157',
      '+55 11 92005-1157'       => '+5511920051157',
      'whatsapp:+5511920051157' => '+5511920051157',
      '1132224444'              => '+551132224444',
      # DDD 55 (Santa Maria/RS): o "55" inicial é o DDD, não o país.
      '55991234567'             => '+5555991234567'
    }.each do |entrada, esperado|
      it "converte #{entrada.inspect} em #{esperado}" do
        expect(described_class.normalizar_telefone(entrada)).to eq(esperado)
      end
    end

    it 'devolve nil para vazio ou número curto demais' do
      expect(described_class.normalizar_telefone(nil)).to be_nil
      expect(described_class.normalizar_telefone('')).to be_nil
      expect(described_class.normalizar_telefone('920051157')).to be_nil
    end
  end

  describe '.canal' do
    it 'prefixa whatsapp:' do
      expect(described_class.canal('11 920051157')).to eq('whatsapp:+5511920051157')
    end

    it 'devolve nil quando o número é inválido' do
      expect(described_class.canal('abc')).to be_nil
    end
  end

  describe 'atributos cifrados' do
    # A tabela é singleton (índice único em singleton_guard) e a migration já
    # cria a linha — por isso os testes que persistem usam `.instancia`.
    it 'grava criptografado e lê de volta em claro' do
      cfg = described_class.instancia
      cfg.update!(smtp_password: 'minha-senha', twilio_auth_token: 'meu-token')
      cfg.reload

      expect(cfg.smtp_password).to eq('minha-senha')
      expect(cfg.twilio_auth_token).to eq('meu-token')
      expect(cfg.smtp_password_cifrado).to be_present
      expect(cfg.smtp_password_cifrado).not_to include('minha-senha')
    end

    it 'não marca o registro como alterado ao reatribuir o mesmo valor' do
      cfg = described_class.instancia
      cfg.update!(smtp_password: 'igual')

      cfg.smtp_password = 'igual'
      expect(cfg).not_to be_changed
    end

    it 'trata segredo ilegível como ausente, sem estourar' do
      cfg = described_class.instancia
      cfg.update!(smtp_password: 'x')
      cfg.update_column(:smtp_password_cifrado, 'lixo-que-nao-decifra')

      expect(cfg.reload.smtp_password).to be_nil
      expect(cfg.smtp_password?).to be(false)
    end
  end

  describe 'validações' do
    it 'não exige SMTP enquanto o envio de e-mail está desligado' do
      expect(described_class.new).to be_valid
    end

    it 'exige servidor, usuário e senha quando o e-mail é ativado' do
      cfg = described_class.new(smtp_ativo: true)
      expect(cfg).not_to be_valid
      expect(cfg.errors.attribute_names).to include(:smtp_address, :smtp_username, :smtp_password)
    end

    it 'exige credenciais do Twilio quando o WhatsApp é ativado' do
      cfg = described_class.new(whatsapp_ativo: true)
      expect(cfg).not_to be_valid
      expect(cfg.errors.attribute_names).to include(:twilio_account_sid, :twilio_auth_token, :twilio_from)
    end

    it 'rejeita e-mail administrativo malformado' do
      expect(described_class.new(email_admin: 'sem-arroba')).not_to be_valid
    end

    it 'rejeita porta fora da faixa' do
      expect(described_class.new(smtp_port: 0)).not_to be_valid
      expect(described_class.new(smtp_port: 70_000)).not_to be_valid
    end

    it 'normaliza os telefones antes de salvar' do
      cfg = described_class.instancia
      cfg.update!(whatsapp_admin: '(11) 92005-1157')
      expect(cfg.reload.whatsapp_admin).to eq('+5511920051157')
    end
  end

  describe '#smtp_settings' do
    it 'devolve nil quando não está pronto, para o .env continuar valendo' do
      expect(described_class.new.smtp_settings).to be_nil
      expect(config_smtp(smtp_ativo: false).smtp_settings).to be_nil
    end

    it 'usa STARTTLS na 587' do
      settings = config_smtp.smtp_settings
      expect(settings[:enable_starttls_auto]).to be(true)
      expect(settings).not_to have_key(:ssl)
      expect(settings[:password]).to eq('segredo123')
      expect(settings[:authentication]).to eq(:plain)
    end

    it 'usa SSL direto na 465' do
      settings = config_smtp(smtp_port: 465).smtp_settings
      expect(settings[:ssl]).to be(true)
      expect(settings).not_to have_key(:enable_starttls_auto)
    end

    it 'deriva o domínio do remetente quando não informado' do
      expect(config_smtp.smtp_settings[:domain]).to eq('reem.com.br')
    end
  end

  describe '#remetente_formatado' do
    it 'monta "Nome" <email> quando há nome' do
      cfg = config_smtp(remetente_email: 'nao-responda@reem.com.br', remetente_nome: 'Reem Transporte')
      expect(cfg.remetente_formatado).to eq('"Reem Transporte" <nao-responda@reem.com.br>')
    end

    it 'cai no usuário do SMTP quando não há remetente próprio' do
      expect(config_smtp.remetente_formatado).to eq('sistema@reem.com.br')
    end
  end

  describe '#credenciais_whatsapp' do
    it 'usa o banco quando está completo' do
      creds = config_whats.credenciais_whatsapp
      expect(creds[:sid]).to eq('AC123')
      expect(creds[:from]).to eq('whatsapp:+14155238886')
    end

    it 'cai no .env quando o banco está incompleto' do
      allow(ENV).to receive(:[]).and_call_original
      allow(ENV).to receive(:[]).with('TWILIO_ACCOUNT_SID').and_return('AC_env')
      allow(ENV).to receive(:[]).with('TWILIO_AUTH_TOKEN').and_return('tok_env')
      allow(ENV).to receive(:[]).with('TWILIO_WHATSAPP_FROM').and_return('+14155238886')

      expect(described_class.new.credenciais_whatsapp[:sid]).to eq('AC_env')
    end

    it 'não mistura SID do banco com token do .env' do
      allow(ENV).to receive(:[]).and_call_original
      allow(ENV).to receive(:[]).with('TWILIO_ACCOUNT_SID').and_return('AC_env')
      allow(ENV).to receive(:[]).with('TWILIO_AUTH_TOKEN').and_return('tok_env')
      allow(ENV).to receive(:[]).with('TWILIO_WHATSAPP_FROM').and_return('+14155238886')

      # SID no banco mas sem token → whatsapp_pronto? é false → vai inteiro pro ENV
      cfg = described_class.new(whatsapp_ativo: true, twilio_account_sid: 'AC_banco')
      expect(cfg.credenciais_whatsapp[:sid]).to eq('AC_env')
      expect(cfg.credenciais_whatsapp[:token]).to eq('tok_env')
    end
  end
end
