# frozen_string_literal: true

class ResultsController < ApplicationController
  include Breadcrumbs
  include TeamsHelper

  before_action :load_my_module
  before_action :load_vars, only: %i(destroy elements assets upload_attachment archive restore destroy
                                     update_view_state update_asset_view_mode update duplicate)
  before_action :check_destroy_permissions, only: :destroy
  before_action :set_breadcrumbs_items, only: %i(index)
  before_action :set_navigator, only: %i(index)
  before_action :set_inline_name_editing, only: %i(index)

  def index
    respond_to do |format|
      format.json do
        # API endpoint
        @results = if params[:view_mode] == 'archived'
                     @my_module.results.archived
                   else
                     @my_module.results.active
                   end

        update_and_apply_user_sort_preference!
        apply_filters!

        @results = @results.page(params.dig(:page, :number) || 1)

        render json: @results, each_serializer: ResultSerializer, scope: current_user,
               meta: { sort: @sort_preference }
      end

      format.html do
        # Main view
        @experiment = @my_module.experiment
        @project = @experiment.project
        render(:index, formats: :html)
      end
    end
  end

  def list
    if params[:with_linked_step_id].present?
      step = @my_module.protocol.steps.find_by(id: params[:with_linked_step_id])
      @results = @my_module.results.where(archived: false).or(@my_module.results.where(id: step.results.select(:id)))
    else
      @results = @my_module.results.active
    end

    update_and_apply_user_sort_preference!
  end

  def create
    result = @my_module.results.create!(user: current_user)
    log_activity(:add_result, { result: result })
    render json: result
  end

  def update
    @result.update!(result_params)
    log_activity(:edit_result, { result: @result })
    render json: @result
  end

  def archive
    if @result.archive(current_user)
      log_activity(:archive_result, { result: @result })
      render json: {}, status: :ok
    else
      render json: { errors: @result.errors.full_messages }, status: :unprocessable_entity
    end
  end

  def restore
    if @result.restore(current_user)
      log_activity(:result_restored, { result: @result })
      render json: {}, status: :ok
    else
      render json: { errors: @result.errors.full_messages }, status: :unprocessable_entity
    end
  end

  def elements
    render json: @result.result_orderable_elements.order(:position),
           each_serializer: ResultOrderableElementSerializer,
           user: current_user
  end

  def assets
    render json: @result.assets.preload(:preview_image_attachment, file_attachment: :blob, result: { my_module: { experiment: :project, user_assignments: %i(user user_role) } }),
           each_serializer: AssetSerializer,
           user: current_user,
           managable_result: can_manage_result?(@result)
  end

  def upload_attachment
    @result.transaction do
      @asset = @result.assets.create!(
        created_by: current_user,
        last_modified_by: current_user,
        team: @my_module.team,
        view_mode: @result.assets_view_mode
      )
      @asset.attach_file_version(params[:signed_blob_id])
      @asset.post_process_file
    end

    log_activity(:result_file_added, { file: @asset.file_name, result: @result })
    render json: @asset,
           serializer: AssetSerializer,
           user: current_user
  end

  def update_view_state
    view_state = @result.current_view_state(current_user)
    view_state.state['assets']['sort'] = params.require(:assets).require(:order)
    view_state.save! if view_state.changed?

    render json: {}, status: :ok
  end

  def update_asset_view_mode
    ActiveRecord::Base.transaction do
      @result.assets_view_mode = params[:assets_view_mode]
      @result.save!(touch: false)
      @result.assets.update_all(view_mode: @result.assets_view_mode)
    end
    render json: { view_mode: @result.assets_view_mode }, status: :ok
  rescue ActiveRecord::RecordInvalid => e
    Rails.logger.error(e.message)
    render json: { errors: e.message }, status: :unprocessable_entity
  end

  def destroy
    name = @result.name
    if @result.discard
      log_activity(:destroy_result, { destroyed_result: name })
      render json: {}, status: :ok
    else
      render json: { errors: @result.errors.full_messages }, status: :unprocessable_entity
    end
  end

  def duplicate
    ActiveRecord::Base.transaction do
      new_result = @result.duplicate(
        @my_module, current_user, result_name: "#{@result.name} (1)"
      )

      log_activity(:result_duplicated, { result: @result })
      render json: new_result, serializer: ResultSerializer, user: current_user
    end
  end

  private

  def result_params
    params.require(:result).permit(:name)
  end

  def update_and_apply_user_sort_preference!
    if params[:sort].present?
      current_user.update_nested_setting(key: 'results_order', id: @my_module.id.to_s, value: params[:sort])
      @sort_preference = params[:sort]
    else
      @sort_preference = current_user.settings.fetch('results_order', {})[@my_module.id.to_s] || 'created_at_desc'
    end
    apply_sort!(@sort_preference)
  end

  def apply_sort!(sort_order)
    case sort_order
    when 'updated_at_asc'
      @results = @results.order('results.updated_at' => :asc)
    when 'updated_at_desc'
      @results = @results.order('results.updated_at' => :desc)
    when 'created_at_asc'
      @results = @results.order('results.created_at' => :asc)
    when 'created_at_desc'
      @results = @results.order('results.created_at' => :desc)
    when 'name_asc'
      @results = @results.order('results.name' => :asc)
    when 'name_desc'
      @results = @results.order('results.name' => :desc)
    end
  end

  def apply_filters!
    if params[:query].present?
      @results = @results.search(current_user, params[:view_mode] == 'archived', params[:query])
                         .page(params[:page] || 1)
                         .per(Constants::SEARCH_LIMIT)
    end

    @results = @results.where('results.created_at >= ?', params[:created_at_from]) if params[:created_at_from]
    @results = @results.where('results.created_at <= ?', params[:created_at_to]) if params[:created_at_to]
    @results = @results.where('results.updated_at >= ?', params[:updated_at_from]) if params[:updated_at_from]
    @results = @results.where('results.updated_at <= ?', params[:updated_at_to]) if params[:updated_at_to]
  end

  def load_my_module
    @my_module = MyModule.readable_by_user(current_user).find(params[:my_module_id])
    current_team_switch(@my_module.team) if current_team != @my_module.team
  end

  def load_vars
    @result = @my_module.results.find(params[:id])

    return render_403 unless @result

    @my_module = @result.my_module
  end

  def check_destroy_permissions
    render_403 unless can_delete_result?(@result)
  end

  def set_navigator
    @navigator = {
      url: tree_navigator_my_module_path(@my_module),
      archived: @my_module.archived_branch?,
      id: @my_module.code
    }
  end

  def log_activity(element_type_of, message_items = {})
    message_items[:my_module] = @my_module.id
    subject = if message_items.key?(:result)
                result = message_items[:result]
                message_items[:result] = result.id
                result
              else
                @my_module
              end

    Activities::CreateActivityService.call(
      activity_type: element_type_of,
      owner: current_user,
      team: @my_module.team,
      subject: subject,
      project: @my_module.experiment.project,
      message_items: message_items
    )
  end

  def set_inline_name_editing
    return unless can_manage_my_module?(@my_module)

    @inline_editable_title_config = {
      name: 'title',
      params_group: 'my_module',
      item_id: @my_module.id,
      field_to_udpate: 'name',
      path_to_update: my_module_path(@my_module)
    }
  end
end
