require 'odata/model'
require 'odata_config'

module ActiveRecordExtension

  extend ActiveSupport::Concern

  def delete
    ::OData::Model.destroy(self)
  end

  def destroy
    destroy!
  rescue
    false
  end

  def destroy!
    run_callbacks :destroy do
      ::OData::Model.destroy(self)
    end
    has_errors = errors.present?
    if has_errors
      raise_record_not_destroyed
    end
    !has_errors
  end

  def save(*)
    save!
  rescue
    false
  end

  def save!(*)
    validate!
    run_callbacks :save do
      ::OData::Model.save(self)
    end
    has_errors = errors.present?
    if has_errors
      raise_record_invalid
    else
      reload unless id.nil?
    end
    !has_errors
  end

  def update(attributes)
    attributes.each do |k,v|
      write_attribute(k,v)
    end
    save
  end

  def update_attribute(name, value)
    write_attribute(name,value)
    save
  end

  # add your static(class) methods here
  module ClassMethods
    def belongs_to_field?(field)
      @belongs_to_fields ||= belongs_to_fields
      @belongs_to_fields.map(&:foreign_key).include?(field)
    end

    def belongs_to_field(field)
      @belongs_to_fields ||= belongs_to_fields
      matching_fields = @belongs_to_fields.select{|f| f.foreign_key == field}
      raise "Multiple matching foreign_keys. Only unique foreign keys allowed [#{field}]" if matching_fields.count > 1
      matching_fields.first
    end

    def belongs_to_field_by_name(name)
      @belongs_to_fields ||= belongs_to_fields
      @belongs_to_fields.select{|f| f.name.to_s == name}.first
    end

    def belongs_to_fields
      reflect_on_all_associations(:belongs_to)
    end

    # If odata refers to table differently than table name when using associations, you can use this method
    def odata_table_reference
      @odata_table_reference
    end

    def odata_table_reference=(value)
      @odata_table_reference = value
    end

    # OData does not allow creating an entry for a table that is used in a many to many joining table. You must
    # associate tables together. If a table uses this field, it indicates its a joining many to many table.
    # An array of the 2 associated tables, eg [Table1, Table2]
    def many_to_many_associated_tables
      @many_to_many_associated_tables
    end

    def many_to_many_associated_tables=(value)
      @many_to_many_associated_tables = value
    end

    # Binding name is found in the metadata xml file. It will look something like (system users - opportunities):
    # <EntitySet Name="systemusers" EntityType="Microsoft.Dynamics.CRM.systemuser">
    #   <NavigationPropertyBinding Path="new_systemuser_opportunity" Target="opportunities"/>
    # Its different for both entities, so this is a hash of tablename to binding name
    def many_to_many_binding_name
      @many_to_many_binding_name
    end

    def many_to_many_binding_name=(value)
      @many_to_many_binding_name = value
    end

    # For some associations the new api does not work, notably the systemuser. But the old api does work.
    # Hopefully in future releases Microsoft will fix the new apis.
    def many_to_many_use_old_api
      @many_to_many_use_old_api ||= false
    end

    def many_to_many_use_old_api=(value)
      @many_to_many_use_old_api = value
    end

    # In some cases the odata field name is different than the database field name. This method is used for this mapping
    def odata_field(field, options)
      @odata_property_key ||= {}
      @odata_property_key[field] = options[:crm_key]
    end

    def odata_field_value(crm_key)
      @odata_property_key ||= {}
      @odata_property_key[crm_key]
    end
  end
end

# You can switch OData off by setting param: 'odata_enabled' to false
unless OdataConfig.odata_config[Rails.env]['odata_enabled'] == false
  # include the extension
  ActiveRecord::Base.send(:include, ActiveRecordExtension)

  # Extend belongs_to for crm_key field
  module BelongsToActiveRecordExtension
    def valid_options
      super + [:crm_key]
    end
  end

  class ActiveRecord::Associations::Builder::BelongsTo
    include ::BelongsToActiveRecordExtension
  end
end