# frozen_string_literal: false

module PGTrunk::Operations::Functions
  # @abstract
  # @private
  # Base class for operations with functions
  class Base < PGTrunk::Operation
    # All attributes that can be used by function-related commands
    attribute :body, :pg_trunk_multiline_text
    attribute :cost, :float
    attribute :language, :pg_trunk_lowercase_string
    attribute :leakproof, :boolean
    attribute :parallel, :pg_trunk_symbol
    attribute :replace_existing, :boolean
    attribute :rows, :integer
    attribute :security, :pg_trunk_symbol
    attribute :strict, :boolean
    attribute :volatility, :pg_trunk_symbol
    attribute :version, :pg_trunk_multiline_text

    # Ensure correctness of present values
    validates :if_not_exists, absence: true
    validates :volatility, inclusion: { in: %i[volatile stable immutable] }, allow_nil: true
    validates :security, inclusion: { in: %i[invoker definer] }, allow_nil: true
    validates :parallel, inclusion: { in: %i[safe unsafe] }, allow_nil: true
    validates :cost, numericality: { greater_than: 0 }, allow_nil: true
    validates :rows, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
    validate do
      errors.add :body, "can't contain SQL injection with $$" if body&.include?("$$")
    end

    # Use comparison by name from pg_trunk operations base class (default)
    # Support name as the only positional argument (default)

    ruby_snippet do |s|
      s.ruby_param(name.lean) if name.present?
      s.ruby_param(to: new_name.lean) if new_name.present?
      s.ruby_param(if_exists: true) if if_exists
      s.ruby_param(force: :cascade) if force == :cascade
      s.ruby_param(replace_existing: true) if replace_existing

      s.ruby_line(:language, language) if language&.!= "sql"
      s.ruby_line(:volatility, volatility, from: from_volatility) if volatility.present?
      s.ruby_line(:leakproof, true) if leakproof
      s.ruby_line(:strict, true) if strict
      s.ruby_line(:security, security) if security.present?
      s.ruby_line(:parallel, parallel, from: from_parallel) unless parallel.nil?
      s.ruby_line(:cost, cost, from: from_cost) if cost.present?
      s.ruby_line(:rows, rows, from: from_rows) if rows.present?
      s.ruby_line(:body, body, from: from_body) if body.present?
      s.ruby_line(:comment, comment, from: from_comment) if comment
    end
  end
end
