module CrudHelpers

  def handle_update(model, id, json, opts = {})
    obj = model.get_or_die(id)
    obj.update_from_json(json, opts)
    updated_response(obj, json)
  end


  def handle_create(model, json, opts = {})
    obj = model.create_from_json(json, opts)

    created_response(obj, json)
  end


  def handle_delete(model, id)
    obj = model.get_or_die(id)
    obj.delete

    deleted_response(id)
  end


  def self.scoped_dataset(model, where_clause)
    dataset = (model.model_scope == :repository) ? model.this_repo : model

    if where_clause.is_a?(Hash) && where_clause.has_key?(:exclude)
      dataset = dataset.exclude(where_clause[:exclude])
      where_clause.delete(:exclude)
    end

    if !where_clause.is_a?(Hash) || !where_clause.empty?
      dataset = dataset.filter(where_clause)
    end

    dataset
  end


  def handle_unlimited_listing(model, where = {})
    dataset = CrudHelpers.scoped_dataset(model, where)

    listing_response(dataset, model)
  end


  def handle_listing(model, pagination_data, where = {}, order = nil)
    dataset = CrudHelpers.scoped_dataset(model, where)
    order = Sequel.send(pagination_data['sort_direction'], pagination_data['sort_field']) unless order
    modified_since_time = Time.at(pagination_data[:modified_since])
    dataset = dataset.where { system_mtime >= modified_since_time }.order(*order)

    if pagination_data[:page]
      # Classic pagination mode
      paginated = dataset.extension(:pagination).paginate(pagination_data[:page], pagination_data[:page_size])

      listing_response(paginated, model)

    elsif pagination_data[:all_ids]
      # Return a JSON array containing all IDs for the matching records
      json_response(dataset.select(:id).map {|rec| rec[:id]})

    elsif pagination_data[:id_set]
      # Return the requested set of IDs
      listing_response(dataset.filter(:id => pagination_data[:id_set]), model)
    end
  end


  def self.with_record_conflict_reporting(model, json)
    begin
      yield
    rescue Sequel::ValidationFailed => e
      if e.errors && e.errors.any? {|key, errors| errors[0].end_with?("must be unique")}
        existing_record = e.errors.any? {|key, errors| errors[0].include?("Authority ID")} ? model.find_matching_id(json) : model.find_matching(json)

        if existing_record
          e.errors[:conflicting_record] = [existing_record.uri]
        end
      end

      raise $!
    end
  end


  def with_record_conflict_reporting(model, json)
    CrudHelpers::with_record_conflict_reporting(model, json) do
      yield
    end
  end


  private

  def listing_response(dataset, model)
    objs = dataset.respond_to?(:all) ? dataset.all : dataset

    opts = {:calculate_linked_repositories => current_user.can?(:index_system)}

    jsons = model.sequel_to_jsonmodel(objs, opts).map {|json|
      if json.is_a?(JSONModelType)
        json.to_hash(:trusted)
      else
        json
      end
    }

    results = resolve_references(jsons, params[:resolve])

    if dataset.respond_to? (:page_range)
      response = {
        :first_page => dataset.page_range.first,
        :last_page => dataset.page_range.last,
        :this_page => dataset.current_page,
        :total => dataset.pagination_record_count,
        :results => results
      }
    else
      response = results
    end

    json_response(response)
  end

end
