# frozen_string_literal: true

class InvalidStatusError < StandardError; end

class FormResponse < ApplicationRecord
  include Discard::Model

  default_scope -> { kept }

  belongs_to :form
  belongs_to :created_by, class_name: 'User'
  belongs_to :submitted_by, class_name: 'User', optional: true
  belongs_to :parent, polymorphic: true, inverse_of: :form_responses

  has_one :step_orderable_element, as: :orderable, dependent: :destroy

  enum :status, { pending: 0, submitted: 1, locked: 2 }

  has_many :form_field_values, dependent: :destroy

  belongs_to :previous_form_response,
             -> { unscope(where: :discarded_at) },
             class_name: 'FormResponse',
             inverse_of: :next_form_response,
             optional: true,
             dependent: :destroy

  has_one  :next_form_response,
           class_name: 'FormResponse',
           foreign_key: 'previous_form_response_id',
           inverse_of: :previous_form_response,
           dependent: :destroy

  def step
    step_orderable_element&.step
  end

  def create_value!(created_by, form_field, value, not_applicable: false)
    ActiveRecord::Base.transaction(requires_new: true) do
      current_form_field_value = form_field_values.where(latest: true).last

      form_field_values.where(form_field: form_field).find_each do |form_field_value|
        form_field_value.update!(latest: false)
      end

      new_form_field_value = "Form#{form_field.data['type']}Value".constantize.new(
        form_field: form_field,
        form_response: self,
        # these can change if the form_response is reset, as submitted_by will be kept the same, but created_by will change
        created_by: created_by,
        submitted_by: created_by,
        submitted_at: DateTime.current,
        not_applicable: not_applicable
      )

      # for RepositoryRowsField we need to directly set previous data to the new value,
      # before save, to perserve existing repository row snapshots
      new_form_field_value.data = current_form_field_value&.data if form_field.data['type'] == 'RepositoryRowsField'

      new_form_field_value.update!(value: value)

      new_form_field_value
    end
  end

  def submit!(user)
    update!(
      status: :submitted,
      submitted_by: user,
      submitted_at: DateTime.current
    )
  end

  def reset!(user)
    raise InvalidStatusError, 'Cannot reset form that has not been submitted yet!' if status != 'submitted'

    ActiveRecord::Base.transaction(requires_new: true) do
      new_form_response = dup
      new_form_response.update!(status: 'pending', created_by: user, previous_form_response: self)

      form_field_values.latest.find_each do |form_field_value|
        form_field_value.dup.update!(form_response: new_form_response, created_by: user)
      end

      # if attached to step, reattach new form response

      discard

      self&.step_orderable_element&.update!(orderable: new_form_response)

      new_form_response
    end
  end

  def duplicate(parent, user, position = nil)
    ActiveRecord::Base.transaction do
      new_form_response = FormResponse.create!(
        status: :pending,
        form_id: form_id,
        discarded_at: nil,
        submitted_by: nil,
        created_by_id: user.id,
        parent: parent
      )

      parent.step_orderable_elements.create!(
        position: position || parent.step_orderable_elements.length,
        orderable: new_form_response
      )

      new_form_response
    end
  end
end
