# frozen_string_literal: true

class FormsController < ApplicationController
  include InputSanitizeHelper

  before_action :check_forms_enabled
  before_action :load_forms, only: %i(index actions_toolbar)
  before_action :load_form, only: %i(show update publish unpublish export_form_responses duplicate)
  before_action :set_breadcrumbs_items, only: %i(index show)
  before_action :check_manage_permissions, only: :update
  before_action :check_create_permissions, only: :create

  def index
    respond_to do |format|
      format.html
      format.json do
        forms_list = Lists::FormsService.new(@forms, params, user: current_user).call
        render json: forms_list,
               each_serializer: Lists::FormSerializer,
               user: current_user,
               meta: pagination_dict(forms_list)
      end
    end
  end

  def show
    respond_to do |format|
      format.json { render json: @form, serializer: FormSerializer, include: %i(form_fields), user: current_user }
      format.html
    end
  end

  def create
    ActiveRecord::Base.transaction do
      @form = Form.new(
        name: I18n.t('forms.default_name'),
        team: current_team,
        created_by: current_user,
        last_modified_by: current_user
      )

      if @form.save
        log_activity(@form, :form_created)
        render json: @form, serializer: FormSerializer, user: current_user
      else
        render json: { error: @form.errors.full_messages }, status: :unprocessable_entity
      end
    end
  end

  def update
    ActiveRecord::Base.transaction do
      if @form.update(form_params.merge({ last_modified_by: current_user }))
        log_activity(@form, :form_name_changed)
        render json: @form, serializer: FormSerializer, user: current_user
      else
        render json: { error: @form.errors.full_messages }, status: :unprocessable_entity
      end
    end
  end

  def published_forms
    forms = current_team.forms.active.readable_by_user(current_user).published.order(name: :asc)
    forms = forms.where('forms.name ILIKE ?', "%#{params[:query]}%") if params[:query].present?
    forms = forms.page(params[:page])

    render json: {
      data: forms.map { |f| [f.id, f.name] },
      paginated: true,
      next_page: forms.next_page
    }
  end

  def latest_attached_forms
    forms = current_team.forms.active.readable_by_user(current_user).published
                        .joins(:form_responses)
                        .where(form_responses: { created_by: current_user })
                        .select('forms.id, forms.name, MAX(form_responses.created_at) AS last_response_at')
                        .group('forms.id')
                        .order('last_response_at DESC')
                        .limit(5)

    render json: {
      data: forms
    }
  end

  def publish
    render_403 and return unless can_publish_form?(@form)

    ActiveRecord::Base.transaction do
      @form.update!(
        published_by: current_user,
        published_on: DateTime.now
      )
      log_activity(@form, :form_published, { version_number: 1 })

      render json: @form, serializer: FormSerializer, user: current_user
    end
  end

  def unpublish
    render_403 and return unless can_unpublish_form?(@form)

    ActiveRecord::Base.transaction do
      @form.update!(
        published_by: nil,
        published_on: nil
      )

      log_activity(@form, :form_unpublished)

      render json: @form, serializer: FormSerializer, user: current_user
    end
  end

  def archive
    forms = current_team.forms.active.where(id: params[:form_ids])
    return render_404 if forms.blank?
    return render_403 unless forms.all? { |f| can_archive_form?(f) }

    counter = 0

    forms.each do |form|
      form.transaction do
        form.archive!(current_user)
        log_activity(form, :form_archived)
        counter += 1
      rescue StandardError => e
        Rails.logger.error e.message
        raise ActiveRecord::Rollback
      end
    end

    if counter.positive?
      render json: { message: t('forms.archived.success_flash', number: counter) }
    else
      render json: { message: t('forms.archived.error_flash') }, status: :unprocessable_entity
    end
  end

  def restore
    forms = current_team.forms.archived.where(id: params[:form_ids])
    return render_404 if forms.blank?
    return render_403 unless forms.all? { |f| can_restore_form?(f) }

    counter = 0

    forms.each do |form|
      form.transaction do
        form.restore!(current_user)
        log_activity(form, :form_restored)
        counter += 1
      rescue StandardError => e
        Rails.logger.error e.message
        raise ActiveRecord::Rollback
      end
    end

    if counter.positive?
      render json: { message: t('forms.restored.success_flash', number: counter) }
    else
      render json: { message: t('forms.restored.error_flash') }, status: :unprocessable_entity
    end
  end

  def duplicate
    ActiveRecord::Base.transaction do
      new_form = @form.duplicate!(current_user)
      log_activity(new_form, :form_duplicated, { form_old: @form.id })
      render json: { message: t('forms.duplicated.success_flash', name: escape_input(@form.name)) }
    rescue ActiveRecord::RecordInvalid
      render json: { error: new_form.errors.full_messages }, status: :unprocessable_entity
      raise ActiveRecord::Rollback
    rescue StandardError => e
      render json: { message: I18n.t('errors.general') }, status: :unprocessable_entity
      Rails.logger.error e.message
      raise ActiveRecord::Rollback
    end
  end

  def export_form_responses
    FormResponsesZipExportJob.perform_later(
      user_id: current_user.id,
      params: {
        form_id: @form.id
      }
    )

    Activities::CreateActivityService.call(
      activity_type: :export_form_responses,
      owner: current_user,
      subject: @form,
      team: @form.team,
      message_items: {
        form: @form.id
      }
    )

    render json: { message: t('zip_export.export_request_success') }
  end

  def actions_toolbar
    selected_forms = @forms.where(id: JSON.parse(params[:items]).pluck('id'))
    render json: {
      actions:
        Toolbars::FormsService.new(
          selected_forms,
          current_user
        ).actions
    }
  end

  private

  def set_breadcrumbs_items
    archived = params[:view_mode] == 'archived' || @form&.archived?

    @breadcrumbs_items = []
    @breadcrumbs_items.push(
      { label: t('breadcrumbs.forms'), url: forms_path(view_mode: archived ? 'archived' : nil) }
    )

    if @form
      @breadcrumbs_items.push(
        { label: @form.name, url: form_path(@form) }
      )
    end

    @breadcrumbs_items.each do |item|
      item[:label] = "#{t('labels.archived')} #{item[:label]}" if archived
    end
  end

  def load_forms
    # Team owners see all forms in the team
    @forms = current_team.forms
    @forms = @forms.readable_by_user(current_user) unless can_manage_team?(current_team)
  end

  def load_form
    @form = current_team.forms.readable_by_user(current_user).find_by(id: params[:id])

    render_404 unless @form
  end

  def check_create_permissions
    render_403 unless can_create_forms?(current_team)
  end

  def check_manage_permissions
    render_403 unless @form && can_manage_form_draft?(@form)
  end

  def form_params
    params.require(:form).permit(:name, :description)
  end

  def check_forms_enabled
    render :promo unless Form.forms_enabled?
  end

  def log_activity(form, type_of, message_items = {})
    Activities::CreateActivityService
      .call(activity_type: type_of,
            owner: current_user,
            team: form.team,
            subject: form,
            message_items: {
              form: form.id,
              user: current_user.id
            }.merge(message_items))
  end
end
