# frozen_string_literal: true

module Decidim
  module Admin
    module Concerns
      # Attachments can be related to any class in Decidim, in order to
      # manage the attachments for a given type, you should create a new
      # controller and include this concern.
      #
      # The only requirement is to define a `attached_to` method that
      # returns an instance of the model to attach the attachment to.
      module HasAttachments
        extend ActiveSupport::Concern

        included do
          helper_method :attached_to, :authorization_object

          def index
            authorize! :read, authorization_object

            render template: "decidim/admin/attachments/index"
          end

          def new
            authorize! :create, authorization_object
            @form = form(AttachmentForm).from_params({}, attached_to: attached_to)
            render template: "decidim/admin/attachments/new"
          end

          def create
            authorize! :create, authorization_object
            @form = form(AttachmentForm).from_params(params, attached_to: attached_to)

            CreateAttachment.call(@form, attached_to) do
              on(:ok) do
                flash[:notice] = I18n.t("attachments.create.success", scope: "decidim.admin")
                redirect_to action: :index
              end

              on(:invalid) do
                flash.now[:alert] = I18n.t("attachments.create.error", scope: "decidim.admin")
                render template: "decidim/admin/attachments/new"
              end
            end
          end

          def edit
            @attachment = collection.find(params[:id])
            authorize! :update, authorization_object
            @form = form(AttachmentForm).from_model(@attachment, attached_to: attached_to)
            render template: "decidim/admin/attachments/edit"
          end

          def update
            @attachment = collection.find(params[:id])
            authorize! :update, authorization_object
            @form = form(AttachmentForm).from_params(attachment_params, attached_to: attached_to)

            UpdateAttachment.call(@attachment, @form) do
              on(:ok) do
                flash[:notice] = I18n.t("attachments.update.success", scope: "decidim.admin")
                redirect_to action: :index
              end

              on(:invalid) do
                flash.now[:alert] = I18n.t("attachments.update.error", scope: "decidim.admin")
                render template: "decidim/admin/attachments/edit"
              end
            end
          end

          def show
            @attachment = collection.find(params[:id])
            authorize! :read, authorization_object
            render template: "decidim/admin/attachments/show"
          end

          def destroy
            @attachment = collection.find(params[:id])
            authorize! :destroy, authorization_object
            @attachment.destroy!

            flash[:notice] = I18n.t("attachments.destroy.success", scope: "decidim.admin")

            redirect_to after_destroy_path
          end

          # Public: Returns a String or Object that will be passed to `redirect_to` after
          # destroying an attachment. By default it redirects to the attached_to.
          #
          # It can be redefined at controller level if you need to redirect elsewhere.
          def after_destroy_path
            attached_to
          end

          # Public: The only method to be implemented at the controller. You need to
          # return the object where the attachment will be attached to.
          def attached_to
            raise NotImplementedError
          end

          # Public: The Class or Object to be used with the authorization layer to
          # verify the user can manage the attachments
          #
          # By default is the same as the attached_to.
          def authorization_object
            attached_to
          end

          def collection
            @collection ||= attached_to.attachments
          end

          private

          def attachment_params
            {
              id: params[:id],
              file: @attachment.file
            }.merge(params[:attachment].to_unsafe_h)
          end
        end
      end
    end
  end
end