class Avo::ResourcesController

def add_link_to_search_results(resources)

def add_link_to_search_results(resources)
  resources.map do |model|
    resource = model.as_json
    resource[:link] = "/resources/#{model.class.to_s.singularize.underscore}/#{model.id}"
    resource
  end
end

def attach

def attach
  attachment_class = App.get_model_class_by_name params[:attachment_name].pluralize 1
  attachment_model = attachment_class.safe_constantize.find params[:attachment_id]
  attached = resource.send(params[:attachment_name]) << attachment_model
  render json: {
    success: true,
    message: "#{attachment_class} attached.",
  }
end

def cast_nullable(params)

def cast_nullable(params)
  fields = avo_resource.get_fields
  nullable_fields = fields.filter { |field| field.nullable }
                          .map { |field| [field.id, field.null_values] }
                          .to_h
  params.each do |key, value|
    nullable = nullable_fields[key.to_sym]
    if nullable.present? && value.in?(nullable)
      params[key] = nil
    end
  end
  params
end

def create

def create
  create_params = resource_params
  # Update the foreign key for a belongs_to relation from a has_many via creation.
  if params[:via_relationship].present?
    relationship_foreign_key = params[:via_resource_name].singularize.camelize.safe_constantize.reflections[params[:via_relationship]].foreign_key
    if create_params[relationship_foreign_key].blank?
      create_params[relationship_foreign_key] = params[:via_resource_id]
    end
  end
  casted_params = cast_nullable(create_params)
  resource = resource_model.new(casted_params)
  resource.save!
  render json: {
    success: true,
    resource: Avo::Resources::Resource.hydrate_resource(resource, avo_resource, :create),
    message: 'Resource created',
  }
end

def destroy

def destroy
  resource.destroy!
  render json: {
    message: 'Resource destroyed',
  }
end

def detach

def detach
  attachment_class = App.get_model_class_by_name params[:attachment_name].pluralize 1
  attachment_model = attachment_class.safe_constantize.find params[:attachment_id]
  attached = resource.send(params[:attachment_name]).delete attachment_model
  render json: {
    success: true,
    message: "#{attachment_class} attached.",
  }
end

def eager_load_files(query)

def eager_load_files(query)
  if avo_resource.attached_file_fields.present?
    avo_resource.attached_file_fields.map(&:id).map do |field|
      query = query.send :"with_attached_#{field}"
    end
  end
  query
end

def edit

def edit
  @view = :edit
  show
end

def fields

def fields
  resource = resource_model.new
  render json: {
    resource: Avo::Resources::Resource.hydrate_resource(resource, avo_resource, :create),
  }
end

def filters

def filters
  avo_filters = avo_resource.get_filters
  filters = []
  avo_filters.each do |filter|
    filters.push(filter.new.render_response)
  end
  render json: {
    filters: filters,
  }
end

def get_filters

def get_filters
  if params[:filters].present?
    return JSON.parse(Base64.decode64(params[:filters]))
  end
  filter_defaults = {}
  avo_resource.get_filters.each do |filter_class|
    filter = filter_class.new
    if filter.default_value.present?
      filter_defaults[filter_class.to_s] = filter.default_value
    end
  end
  filter_defaults
end

def index

def index
  params[:page] ||= 1
  params[:per_page] ||= Avo.configuration.per_page
  params[:sort_by] = params[:sort_by].present? ? params[:sort_by] : :created_at
  params[:sort_direction] = params[:sort_direction].present? ? params[:sort_direction] : :desc
  filters = get_filters
  if params[:via_resource_name].present? and params[:via_resource_id].present? and params[:via_relationship].present?
    # get the reated resource (via_resource)
    related_resource = App.get_resource_by_name(params[:via_resource_name])
    related_model = related_resource.model
    # fetch the entries
    query = related_model.find(params[:via_resource_id]).public_send(params[:via_relationship])
    params[:per_page] = Avo.configuration.via_per_page
  elsif ['has_many', 'has_and_belongs_to_many'].include? params[:for_relation]
    resources = resource_model.all.map do |model|
      {
        value: model.id,
        label: model.send(avo_resource.title),
      }
    end
    return render json: {
      resources: resources
    }
  else
    query = resource_model
  end
  query = query.order("#{params[:sort_by]} #{params[:sort_direction]}")
  if filters.present?
    filters.each do |filter_class, filter_value|
      query = filter_class.safe_constantize.new.apply_query request, query, filter_value
    end
  end
  # Eager load the attachments
  query = eager_load_files(query)
  # Eager lood the relations
  if avo_resource.includes.present?
    query = query.includes(*avo_resource.includes)
  end
  resources = query.page(params[:page]).per(params[:per_page])
  resources_with_fields = []
  resources.each do |resource|
    resources_with_fields << Avo::Resources::Resource.hydrate_resource(resource, avo_resource, :index)
  end
  meta = {
    per_page_steps: Avo.configuration.per_page_steps,
    available_view_types: avo_resource.available_view_types,
    default_view_type: avo_resource.default_view_type || Avo.configuration.default_view_type,
  }
  render json: {
    success: true,
    meta: meta,
    resources: resources_with_fields,
    per_page: params[:per_page],
    total_pages: resources.total_pages,
  }
end

def permitted_params

def permitted_params
  permitted = avo_resource.get_fields.select(&:updatable).map do |field|
    # If it's a relation
    if field.methods.include? :foreign_key
      database_id = field.foreign_key(avo_resource.model)
    end
    if database_id.present?
      # Allow the database_id for belongs_to relation
      database_id.to_sym
    elsif field.is_array_param
      # Allow array param if necessary
      { "#{field.id}": [] }
    elsif field.is_object_param
      # Allow array param if necessary
      [:"#{field.id}", "#{field.id}": {} ]
    else
      field.id.to_sym
    end
  end
  permitted
end

def process_file_field(field, attachment)

def process_file_field(field, attachment)
  if attachment.is_a? ActionDispatch::Http::UploadedFile
    # New file has been attached
    field.attach attachment
  elsif attachment.blank?
    # File has been deleted
    field.purge
  elsif attachment.is_a? String
    # Field is unchanged
  end
end

def resource

def resource
  eager_load_files(resource_model).find params[:id]
end

def resource_params

def resource_params
  params.require(:resource).permit(permitted_params)
end

def search

def search
  if params[:resource_name].present?
    resources = add_link_to_search_results search_resource(avo_resource)
  else
    resources = []
    resources_to_search_through = App.get_resources.select { |r| r.search.present? }
    resources_to_search_through.each do |resource_model|
      found_resources = add_link_to_search_results search_resource(resource_model)
      resources.push({
        label: resource_model.name,
        resources: found_resources
      })
    end
  end
  render json: {
    resources: resources
  }
end

def search_resource(avo_resource)

def search_resource(avo_resource)
  avo_resource.query_search(query: params[:q], via_resource_name: params[:via_resource_name], via_resource_id: params[:via_resource_id])
end

def show

def show
  render json: {
    resource: Avo::Resources::Resource.hydrate_resource(resource, avo_resource, @view || :show),
  }
end

def update

def update
  update_file_fields
  # Filter out the file params
  regular_resource_params = resource_params.select do |id, value|
    !avo_resource.attached_file_fields.map(&:id).to_s.include? id
  end
  if avo_resource.has_devise_password and regular_resource_params[:password].blank?
    regular_resource_params.delete(:password_confirmation)
    regular_resource_params.delete(:password)
  end
  casted_params = cast_nullable(regular_resource_params)
  avo_resource.fill_model(resource, casted_params).save!
  render json: {
    success: true,
    resource: Avo::Resources::Resource.hydrate_resource(resource, avo_resource, :show),
    message: 'Resource updated',
  }
end

def update_file_fields

def update_file_fields
  # Pick and attach file fields
  attached_file_fields = avo_resource.attached_file_fields
  if attached_file_fields.present?
    file_fields_params = {}
    # Map params to fields
    attached_file_fields.each do |field|
      file_fields_params[field.id] = resource_params[field.id.to_sym]
    end
    file_fields_params.each do |id, attachment|
      # Identify field to make it easier to work with
      field = resource.send(id)
      if file_fields_params[id].is_a? Array
        # Get the ID's that we kept
        file_ids_kept = file_fields_params[id].select { |attachment| attachment.is_a? String }.map(&:to_i)
        # Compute the difference
        to_remove = field.pluck(:id) - file_ids_kept
        # Remove the missing ones
        to_remove.map { |attachment_id| field.find(attachment_id).purge }
      end
      # Figure out what has been submitted
      if attachment.is_a? Array
        # Files have been attached
        attachment.each do |attachment|
          process_file_field field, attachment
        end
      else
        process_file_field field, attachment
      end
    end
  end
end