# -*- encoding: utf-8 -*-

require 'aequitas/exceptions'
require 'aequitas/support/value_object'
require 'aequitas/message_transformer'

module Aequitas
  class Violation
    extend Aequitas::ValueObject

    equalize_on :resource, :rule, :custom_message, :attribute_name

    def self.default_transformer
      @default_transformer ||= MessageTransformer.default
    end

    def self.default_transformer=(transformer)
      @default_transformer = transformer
    end

    attr_reader :resource
    attr_reader :custom_message
    attr_reader :rule
    attr_reader :attribute_name

    # Configure a Violation instance
    # 
    # @param [Object] resource
    #   the validated object
    # @param [String, #call, Hash] message
    #   an optional custom message for this Violation
    # @param [Rule] rule
    #   the Rule whose violation triggered the creation of the receiver
    # @param [Symbol] attribute_name
    #   the name of the attribute whose validation rule was violated
    #   or nil, if a Rule was provided.
    # 
    def initialize(resource, message = nil, rule = nil, attribute_name = nil)
      unless message || rule
        raise ArgumentError, "expected +message+ or +rule+"
      end

      @resource       = resource
      @rule           = rule
      @attribute_name = attribute_name
      @custom_message = evaluate_message(message)
    end

    # @api public
    def message(transformer = Undefined)
      return @custom_message if @custom_message

      transformer = self.transformer if Undefined.equal?(transformer)

      transformer.transform(self)
    end

    # @api public
    alias_method :to_s, :message

    # @api public
    def attribute_name
      if @attribute_name
        @attribute_name
      elsif rule
        rule.attribute_name
      end
    end

    # @api public
    def type
      rule ? rule.violation_type(resource) : nil
    end

    # @api public
    def info
      rule ? rule.violation_info(resource) : { }
    end

    def values
      rule ? rule.violation_values(resource) : [ ]
    end

    def transformer
      if resource.respond_to?(:validation_rules) && transformer = resource.validation_rules.transformer
        transformer
      else
        Violation.default_transformer
      end
    end

    def evaluate_message(message)
      if message.respond_to?(:call)
        if resource.respond_to?(:model) && resource.model.respond_to?(:properties)
          property = resource.model.properties[attribute_name]
          message.call(resource, property)
        else
          message.call(resource)
        end
      else
        message
      end
    end

    # In general we want Aequitas::ValueObject-type equality/equivalence,
    # but this allows direct equivalency test against Strings, which is handy
    def ==(other)
      if other.respond_to?(:to_str)
        self.to_s == other.to_str
      else
        super
      end
    end

  end # class Violation
end # module Aequitas