# frozen_string_literal: true

class GpoVerifyForm
  include ActiveModel::Model

  validates :otp, presence: true
  validate :validate_otp
  validate :validate_otp_not_expired
  validate :validate_pending_profile

  attr_accessor :otp, :pii, :pii_attributes
  attr_reader :attempts_api_tracker, :user, :resolved_authn_context_result, :fraud_ops_tracker

  def initialize(attempts_api_tracker:, user:, pii:, resolved_authn_context_result:,
                 fraud_ops_tracker:, otp: nil)
    @attempts_api_tracker = attempts_api_tracker
    @user = user
    @pii = pii
    @resolved_authn_context_result = resolved_authn_context_result
    @otp = otp
    @fraud_ops_tracker = fraud_ops_tracker
  end

  def submit
    result = valid?
    fraud_check_failed = pending_profile&.fraud_pending_reason.present?
    reproof = user.has_proofed_before?

    if result
      pending_profile&.remove_gpo_deactivation_reason

      if fraud_check_failed && threatmetrix_enabled?
        pending_profile&.deactivate_for_fraud_review
      elsif fraud_check_failed
        pending_profile&.activate_after_fraud_review_unnecessary
      else
        activate_profile
      end
    else
      reset_sensitive_fields
    end

    if pending_profile&.active?
      attempts_api_tracker.idv_enrollment_complete(reproof:)
      fraud_ops_tracker.idv_enrollment_complete(reproof:)
    end

    FormResponse.new(
      success: result,
      errors: errors,
      extra: {
        profile_age_in_seconds: pending_profile&.profile_age_in_seconds,
        enqueued_at: gpo_confirmation_code&.code_sent_at,
        which_letter: which_letter,
        letter_count: letter_count,
        submit_attempts: submit_attempts,
        pii_like_keypaths: [[:errors, :otp], [:error_details, :otp]],
        pending_in_person_enrollment: !!pending_profile&.in_person_enrollment&.pending?,
        fraud_check_failed: fraud_check_failed,
        initiating_service_provider: pending_profile&.initiating_service_provider_issuer,
      },
    )
  end

  private

  def pending_profile
    @pending_profile ||= user.pending_profile
  end

  def gpo_confirmation_code
    return if otp.blank? || pending_profile.blank?

    pending_profile.gpo_confirmation_codes.first_with_otp(otp)
  end

  def schedule_in_person_enrollment_and_deactivate_profile(is_enhanced_ipp)
    UspsInPersonProofing::EnrollmentHelper.schedule_in_person_enrollment(
      user:,
      pii:,
      is_enhanced_ipp:,
    )
    pending_profile&.deactivate_for_in_person_verification
  end

  def which_letter
    return if !valid_otp?
    pending_profile.gpo_confirmation_codes
      .sort_by(&:code_sent_at)
      .index(gpo_confirmation_code) + 1
  end

  def letter_count
    pending_profile&.gpo_confirmation_codes&.count
  end

  def submit_attempts
    RateLimiter.new(user: user, rate_limit_type: :verify_gpo_key).attempts
  end

  def validate_otp_not_expired
    return unless gpo_confirmation_code.present? && gpo_confirmation_code.expired?

    if user_can_request_another_letter?
      errors.add :otp, :gpo_otp_expired
    else
      errors.add :otp, :gpo_otp_expired_and_cannot_request_another
    end
  end

  def validate_pending_profile
    errors.add :base, :no_pending_profile, type: :no_pending_profile unless pending_profile
  end

  def validate_otp
    return if otp.blank? || valid_otp?
    errors.add :otp, :confirmation_code_incorrect, type: :confirmation_code_incorrect
  end

  def valid_otp?
    otp.present? && gpo_confirmation_code.present?
  end

  def reset_sensitive_fields
    self.otp = nil
  end

  def threatmetrix_enabled?
    FeatureManagement.proofing_device_profiling_decisioning_enabled?
  end

  def fraud_review_checker
    @fraud_review_checker ||= FraudReviewChecker.new(user)
  end

  def activate_profile
    pending_profile&.remove_gpo_deactivation_reason
    pending_profile&.activate
  end

  def user_can_request_another_letter?
    policy = Idv::GpoVerifyByMailPolicy.new(user, resolved_authn_context_result)
    policy.resend_letter_available?
  end
end
