require 'bundler/inline'

gemfile do
  source 'https://rubygems.org'

  # NOTE: I used an older version of the Activemodel only to show the compatibility with its older versions.
  gem 'activemodel', '~> 3.2', '>= 3.2.22.5'
  gem 'u-service', '~> 0.12.0'
end

require 'micro/service/with_validation'

module Users
  class Entity
    include Micro::Attributes.with(:initialize)

    attributes :id, :name, :email

    def persisted?
      !id.nil?
    end
  end

  module Creation
    require 'uri'
    require 'securerandom'

    class ProcessParams < Micro::Service::Base
      attributes :name, :email

      def call!
        Success(name: normalized_name, email: String(email).downcase.strip)
      end

      private def normalized_name
        String(name).strip.gsub(/\s+/, ' ')
      end
    end

    class ValidateParams < Micro::Service::WithValidation
      attributes :name, :email

      validates :name, presence: true
      validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }

      def call!
        Success(attributes(:name, :email))
      end
    end

    class Persist < Micro::Service::Base
      attributes :name, :email

      def call!
        Success(user: Entity.new(user_data))
      end

      private def user_data
        attributes.merge(id: SecureRandom.uuid)
      end
    end

    class SendToCRM < Micro::Service::Base
      attribute :user

      def call!
        return Success(user_id: user.id, crm_id: send_to_crm) if user.persisted?

        Failure(:crm_error) { 'User can\'t be sent to the CRM' }
      end

      private def send_to_crm
        # Do some integration stuff...
        SecureRandom.uuid
      end
    end

    Process = ProcessParams >> ValidateParams >> Persist >> SendToCRM
  end
end

require 'pp'

params = {
  "name" => "  Rodrigo  \n  Serradura ",
  "email" => "   RoDRIGo.SERRAdura@gmail.com   "
}

#--------------------------------------#
puts "\n-- Parameters processing --\n\n"
#--------------------------------------#

print 'Before: '
p params

print ' After: '
Users::Creation::ProcessParams.call(params).on_success { |value| p value }

#---------------------------------#
puts "\n-- Success scenario --\n\n"
#---------------------------------#

Users::Creation::Process
  .call(params)
  .on_success do |user_id:, crm_id:|
    puts " CRM ID: #{crm_id}"
    puts "USER ID: #{user_id}"
  end

#---------------------------------#
puts "\n-- Failure scenario --\n\n"
#---------------------------------#

Users::Creation::Process
  .call(name: '', email: '')
  .on_failure { |errors:| p errors.full_messages }


# :: example of the output: ::

# -- Parameters processing --
#
# Before: {"name"=>"  Rodrigo  \n  Serradura ", "email"=>"   RoDRIGo.SERRAdura@gmail.com   "}
#  After: {:name=>"Rodrigo Serradura", :email=>"rodrigo.serradura@gmail.com"}
#
# -- Success scenario --
#
#  CRM ID: f3f189f6-ba6a-40ab-998c-c86773c41c83
# USER ID: c140ffc3-6a7c-4554-972a-c2a0d59f8cb1
#
# -- Failure scenarios --
#
# ["Name can't be blank", "Email is invalid"]