require_dependency "binda/application_controller"

module Binda
  class FieldGroupsController < ApplicationController
    before_action :set_structure
    before_action :set_field_group, only: [:show, :edit, :update, :destroy, :add_field_setting]

    def index
      redirect_to structure_field_group_path( @structure.slug )
    end

    def show
      redirect_to action: :edit
    end

    def new
      @field_group = @structure.field_groups.build()
    end

    def edit
    end

    def create
      @field_group = @structure.field_groups.build(field_group_params)

      if @field_group.save
        reset_field_settings_cache
        redirect_to structure_field_group_path( @structure.slug, @field_group.slug ), notice: 'Field group was successfully created.'
      else
        redirect_to new_structure_field_group_path( @structure.slug ), flash: { alert: @field_group.errors }
      end
    end

    def update
      # Add nested classes
      add_new_choices
      check_if_needs_to_update_choices

      # Update the other ones
      if @field_group.update(field_group_params)
        reset_field_settings_cache
        redirect_to structure_field_group_path( @structure.slug, @field_group.slug ), notice: 'Field group was successfully updated.'
      else
        redirect_to edit_structure_field_group_path( @structure.slug, @field_group.slug ), flash: { alert: @field_group.errors }
      end
    end

    def destroy
      @field_group.destroy!
      reset_field_settings_cache
      if params[:isAjax]
        render json: { target_id: params[:target_id] }, status: 200
      else
        redirect_to structure_path( @structure.slug ), notice: 'Field group was successfully destroyed along with all dependents.'
      end
    end

    def sort
      params[:field_group].each_with_index do |id, i|
        FieldGroup.find( id ).update_column('position', i+1) # use update_column to skip callbacks (which leads to huge useless memory consumption)
      end
      render json: { id: "##{params[:id]}" }, status: 200
    end

    def sort_field_settings
      sort_field_setting_by(params["form--list-item"])
      render json: { id: "##{params[:id]}" }, status: 200
    end

    def add_field_setting
      # We set some default values in order to be able to save the field setting
      # (if field setting isn't save it makes impossible to sort the order)
      # TODO params should be passed into a permit method
      @field_setting = FieldSetting.new(
        name: "#{I18n.t('binda.field_setting.new')}",
        field_group_id: @field_group.id, 
        field_type: 'string',
        ancestry: params[:ancestry]
      )
      @field_setting.save!
      render 'binda/field_groups/_form_new_item', layout: false
    end

    private
      # Use callbacks to share common setup or constraints between actions.
      def set_structure
        @structure = Structure.friendly.find( params[:structure_id] )
      end

      def set_field_group
        @field_group = FieldGroup.friendly.find(params[:id])
      end

      # Only allow a trusted parameter "white list" through.
      def field_group_params
        params.require(:field_group).permit(
          :name, :slug, :description, :position, :layout, :structure_id, field_settings_attributes: [ 
            :id, :field_group_id, :field_setting_id, :name, :slug, :description, :field_type, :position, :required, :read_only, :default_text, :ancestry, :default_choice_id, :allow_null, accepted_structure_ids: [], choices: [], choices_attributes: [ 
              :id, :field_setting_id, :label, :value 
            ]
          ]
        )
      end

      def new_params
        params.require(:field_group).permit( 
          new_field_settings: [ 
            :id, :field_group_id, :field_setting_id, :name, :slug, :description, :field_type, :position, :required, :read_only, :ancestry, :default_choice_id, :allow_null, choices: [] 
          ], 
          new_choices: [ 
            :id, :field_setting_id, :label, :value 
          ]
        )
      end

      def reset_field_settings_cache
        FieldSetting.reset_field_settings_array
      end

      def add_new_choices
        # Create new fields if any
        return if new_params[:new_choices].nil? 
        new_params[:new_choices].each do |choice|
          next if choice[:label].blank? || choice[:value].blank?
          create_new_choice choice
        end
      end

      # Create new choice (depends directly from add_new_choice method)
      def create_new_choice choice
        new_choice = Choice.create( choice )
        unless new_choice
          return redirect_to edit_structure_field_group_path( @structure.slug, @field_group.slug ), flash: { error: new_choice.errors }
        end
      end

      def check_if_needs_to_update_choices
        return if field_group_params[:field_settings_attributes].nil?
        field_group_params[:field_settings_attributes].each do |_, field_setting_params|
          next if field_setting_params[:choices_attributes].nil?
          update_field_setting_choices field_setting_params
        end
      end

      def update_field_setting_choices field_setting_params
        field_setting_params[:choices_attributes].each do |_, choice_params|
          choice = Choice.find(choice_params[:id])
          unless choice.update(choice_params)
            return redirect_to edit_structure_field_group_path( @structure.slug, @field_group.slug ), flash: { error: choice.errors }
          end
        end
      end

      # Sort field settings following the order with which are listed in the array provided as a argument.
      #
      # @param field_settings [Array] the list of ids of the field settings
      def sort_field_setting_by(field_settings)
        field_settings.each_with_index do |id, i|
          FieldSetting.find( id ).update_column('position', i+1)
        end
      end
  end
end
