# frozen_string_literal: true
class CustomWizard::Submission
  include ActiveModel::SerializerSupport

  PAGE_LIMIT = 50
  KEY ||= "submissions"
  META ||= %w[updated_at submitted_at route_to redirect_on_complete redirect_to]

  attr_reader :id, :wizard

  attr_accessor :fields, :permitted_param_keys

  META.each { |attr| class_eval { attr_accessor attr } }

  def initialize(wizard, data = {})
    @wizard = wizard

    data = (data || {}).with_indifferent_access
    @id = data["id"] || SecureRandom.hex(12)
    non_field_keys = META + ["id"]
    @fields = data.except(*non_field_keys) || {}

    META.each { |attr| send("#{attr}=", data[attr]) if data[attr] }

    @permitted_param_keys = data["permitted_param_keys"] || []
  end

  def save
    return nil unless wizard.save_submissions
    validate

    submission_list = self.class.list(wizard)
    submissions = submission_list.submissions.select { |submission| submission.id != self.id }
    self.updated_at = Time.now.iso8601
    submissions.push(self)

    submission_data = submissions.map { |submission| data_to_save(submission) }
    PluginStore.set("#{wizard.id}_#{KEY}", wizard.actor_id, submission_data)
  end

  def validate
    self.fields = fields.select { |key, value| validate_field_key(key) }
  end

  def validate_field_key(key)
    wizard.field_ids.include?(key) || wizard.action_ids.include?(key) ||
      permitted_param_keys.include?(key)
  end

  def fields_and_meta
    result = fields

    META.each do |attr|
      if value = self.send(attr)
        result[attr] = value
      end
    end

    result
  end

  def present?
    fields_and_meta.present?
  end

  def data_to_save(submission)
    data = { id: submission.id }

    data.merge!(submission.fields_and_meta)

    if submission.permitted_param_keys.present?
      data[:permitted_param_keys] = submission.permitted_param_keys
    end

    data
  end

  def submitted?
    !!submitted_at
  end

  def self.get(wizard)
    data = PluginStore.get("#{wizard.id}_#{KEY}", wizard.actor_id).last
    new(wizard, data)
  end

  def remove
    if present?
      data = PluginStore.get("#{@wizard.id}_#{KEY}", wizard.actor_id)
      data.delete_if { |sub| sub["id"] == @id }
      PluginStore.set("#{@wizard.id}_#{KEY}", wizard.actor_id, data)
    end
  end

  def self.cleanup_incomplete_submissions(wizard)
    all_submissions = list(wizard)
    sorted_submissions =
      all_submissions
        .submissions
        .sort_by do |submission|
          zero_epoch_time = DateTime.strptime("0", "%s")
          [
            submission.submitted_at ? Time.iso8601(submission.submitted_at) : zero_epoch_time,
            submission.updated_at ? Time.iso8601(submission.updated_at) : zero_epoch_time,
          ]
        end
        .reverse

    has_incomplete = false
    valid_submissions =
      sorted_submissions.select do |submission|
        to_be_included = submission.submitted_at || !has_incomplete
        has_incomplete = true if !submission.submitted_at

        to_be_included
      end

    valid_data = valid_submissions.map { |submission| submission.data_to_save(submission) }
    PluginStore.set("#{wizard.id}_#{KEY}", wizard.actor_id, valid_data)
  end

  def self.list(wizard, order_by: nil, page: nil)
    list_actor_id = wizard.actor_id
    list_user = wizard.user if wizard.user.present?

    params = { plugin_name: "#{wizard.id}_#{KEY}" }
    params[:key] = list_actor_id if list_actor_id

    query = PluginStoreRow.where(params)
    query = query.order(order_by) if order_by

    result = OpenStruct.new(submissions: [], total: nil)

    query.each do |record|
      if (submission_data = ::JSON.parse(record.value)).any?
        submission_user = list_user || User.find_by(id: record.key.to_i)

        submission_data.each do |data|
          _wizard = wizard.clone
          _wizard.user = submission_user if submission_user.present?
          result.submissions.push(new(_wizard, data))
        end
      end
    end

    result.total = result.submissions.size
    result.submissions.sort_by! { |h| [h.submitted_at ? 1 : 0, h.submitted_at] }.reverse!

    if !page.nil?
      start = page * PAGE_LIMIT
      length = PAGE_LIMIT

      if result.submissions.length > start
        result.submissions = result.submissions[start, length]
      else
        result.submissions = []
      end
    end

    result
  end
end
