# Core
require "controlled_versioning/revision"
require "controlled_versioning/revision_auditor"
require "controlled_versioning/revision_builder"
require "controlled_versioning/revision_accepter"
require "controlled_versioning/initial_version_builder"

# Add utility classes
require "support/array_converter"
require "support/attribute_encapsulator"

module ControlledVersioning
  module ActsAsVersionable
    extend ActiveSupport::Concern
 
    included do
    end
 
    module ClassMethods
      def acts_as_versionable(options = {})

        send :include, InstanceMethods

        def set_versionable_attribute_names(nonversionables)
          nonversionables = [] unless nonversionables.present?
          ArrayConverter.to_s!(nonversionables)
          nonversionables += ["id", "updated_at", "created_at"]
          attribute_names - nonversionables
        end

        def nested_associations
          nested_attributes_options.keys
        end

        def has_nested_associations?
          nested_associations.present?
        end

        attr_accessor :user

        after_create :create_initial_version

        cattr_accessor :nested_within
        self.nested_within = options[:nested_within]

        def is_a_nested_association?
          nested_within.present?
        end

        if is_a_nested_association?
          has_many :version_children,
                   class_name: 'ControlledVersioning::VersionChild',
                   as: :versionable
        else
          has_many :versions,
                   lambda { order("created_at ASC")},
                   class_name: 'ControlledVersioning::Version',
                   as: :versionable
        end

        cattr_accessor :versionable_attribute_names
        self.versionable_attribute_names = options[:versionable_attributes] ||
                                           set_versionable_attribute_names(
                                           options[:nonversionable_attributes])
      end
    end

    module InstanceMethods
      def is_a_nested_association?
        self.class.is_a_nested_association?
      end

      def versionable_attributes
        versionable_attributes = self.versionable_attribute_names
        ArrayConverter.to_s!(versionable_attributes)
        self.attributes.slice(*versionable_attributes)
      end

      def initial_version
        if is_a_nested_association?
          version_children.first
        else
          versions.find_by(initial: true)
        end
      end

      def submit_revision(suggested_attributes)
        assign_attributes(suggested_attributes)
        if invalid?
          errors
        elsif !RevisionAuditor.new(self).changes_original?
          errors[:base] << I18n.t("errors.messages.no_revisions_made")
          errors
        else
          version = versions.build
          RevisionBuilder.new(versionable: self, version: version).version
          version.save
          version
        end
      end

      private
      def create_initial_version
        InitialVersionBuilder.new(self)
      end
    end
  end
end

ActiveRecord::Base.send :include, ControlledVersioning::ActsAsVersionable