class SuggestedEditController < ApplicationController
  before_action :set_suggested_edit, only: [:show, :approve, :reject]

  def category_index
    @show_decided = params[:show_decided].present? && params[:show_decided] == '1'

    sort_orders = { asc: :asc, desc: :desc }
    sort_types = { age: :created_at, decided: :decided_at }

    @default_sort_order = :desc
    @default_sort_type = @show_decided ? :decided : :age
    @sort_order = sort_orders[params[:order]&.to_sym] || sort_orders[@default_sort_order]
    @sort_type = sort_types[params[:sort]&.to_sym] || sort_types[@default_sort_type]

    @category = params[:category].present? ? Category.find(params[:category]) : nil

    @edits = SuggestedEdit.where(post: Post.undeleted.in(@category),
                                 active: !@show_decided)
                          .order(@sort_type => @sort_order)
  end

  def show
    render layout: 'without_sidebar'
  end

  def approve
    unless @edit.active?
      render json: { status: 'error', message: 'This edit has already been reviewed.' }, status: :conflict
      return
    end

    @post = @edit.post

    unless current_user&.can_approve?(@edit)
      render(json: { status: 'error',
                     message: helpers.ability_err_msg(:edit_posts, 'review suggested edits') },
             status: :forbidden)
      return
    end

    comment = params[:comment].present? && !params[:comment].empty? ? params[:comment] : @edit.comment

    # The to_a / dup methods called on the tags for `opts` and `before` are necessary.
    # We need to work on a copy of them, because we update the post before the edit, which will change their values.
    # (We would otherwise be pointing to the same instance, and only see the updated version).
    opts = { before: @post.body_markdown,
             after: @edit.body_markdown,
             comment: comment,
             before_title: @post.title,
             after_title: @edit.title,
             before_tags: @post.tags.to_a,
             after_tags: @edit.tags }

    before = before_attributes(@post)

    @post.transaction do
      post_update_status = @post.update(applied_details)

      if post_update_status
        edit_update_status = @edit.update(before.merge(active: false,
                                                       accepted: true,
                                                       comment: comment,
                                                       rejected_comment: '',
                                                       decided_at: DateTime.now,
                                                       decided_by: current_user,
                                                       updated_at: DateTime.now))

        if @edit.errors.any?
          @post.errors.merge!(@edit.errors)
          raise ActiveRecord::Rollback
        end

        if edit_update_status
          PostHistory.post_edited(@post, @edit.user, **opts)
          AbilityQueue.add(@edit.user, "Suggested Edit Approved ##{@edit.id}")
        end
      end

      next
    end

    if @post.errors.any?
      render json: { status: 'error', message: @post.errors.full_messages.join(', ') }, status: :bad_request
    else
      flash[:success] = 'Edit approved successfully.'
      render json: { status: 'success', redirect_url: post_path(@post) }
    end
  end

  def reject
    unless @edit.active?
      render json: { status: 'error',
                     message: 'This edit has already been reviewed.' },
             status: :conflict
      return
    end

    @post = @edit.post

    unless current_user&.can_reject?(@edit)
      render json: { status: 'error',
                     message: helpers.ability_err_msg(:edit_posts, 'review suggested edits') },
             status: :forbidden
      return
    end

    before = before_attributes(@post)

    if @edit.update(before.merge(active: false,
                                 accepted: false,
                                 rejected_comment: params[:rejection_comment],
                                 decided_at: DateTime.now,
                                 decided_by: current_user,
                                 updated_at: DateTime.now))

      flash[:success] = 'Edit rejected successfully.'
      AbilityQueue.add(@edit.user, "Suggested Edit Rejected ##{@edit.id}")
      render json: {
        status: 'success',
        redirect_url: helpers.generic_share_link(@post)
      }
    else
      render json: { status: 'error',
                     message: 'Cannot reject this suggested edit... Strange.' },
             status: :internal_server_error
    end
  end

  private

  def set_suggested_edit
    @edit = SuggestedEdit.find(params[:id])
  end

  def applied_details
    {
      title: @edit.title,
      tags_cache: @edit.tags_cache&.reject(&:empty?),
      body: @edit.body,
      body_markdown: @edit.body_markdown,
      last_activity: DateTime.now,
      last_activity_by: @edit.user,
      last_edited_at: DateTime.now,
      last_edited_by: @edit.user
    }.compact
  end

  def before_attributes(post)
    { before_body: post.body,
      before_body_markdown: post.body_markdown,
      before_tags_cache: post.tags_cache.dup,
      before_tags: post.tags.to_a,
      before_title: post.title }
  end
end
