# frozen_string_literal: true

module Idv
  class EnterPasswordController < ApplicationController
    include Idv::AvailabilityConcern
    include IdvStepConcern
    include StepIndicatorConcern
    include VerifyByMailConcern
    include IppHelper

    before_action :confirm_step_allowed
    before_action :confirm_no_profile_yet
    before_action :confirm_current_password, only: [:create]

    helper_method :step_indicator_step

    rescue_from UspsInPersonProofing::Exception::RequestEnrollException,
                with: :handle_request_enroll_exception

    def new
      Funnel::DocAuth::RegisterStep.new(current_user.id, current_sp&.issuer)
        .call(:encrypt, :view, true)
      analytics.idv_enter_password_visited(
        address_verification_method: idv_session.address_verification_mechanism,
        **ab_test_analytics_buckets,
      )

      @title = title
      @heading = heading

      @verify_by_mail = idv_session.verify_by_mail?
    end

    def create
      clear_future_steps!

      init_profile

      flash[:success] =
        if idv_session.verify_by_mail?
          t('idv.messages.gpo.letter_on_the_way')
        else
          t('idv.messages.confirm')
        end

      redirect_to next_step

      analytics.idv_enter_password_submitted(
        success: true,
        fraud_review_pending: idv_session.profile.fraud_review_pending?,
        fraud_rejection: idv_session.profile.fraud_rejection?,
        fraud_pending_reason: idv_session.profile.fraud_pending_reason,
        gpo_verification_pending: idv_session.profile.gpo_verification_pending?,
        in_person_verification_pending: idv_session.profile.in_person_verification_pending?,
        deactivation_reason: idv_session.profile.deactivation_reason,
        proofing_workflow_time_in_seconds: idv_session.proofing_workflow_time_in_seconds,
        **ab_test_analytics_buckets,
      )
      Funnel::DocAuth::RegisterStep.new(current_user.id, current_sp&.issuer)
        .call(:verified, :view, true)
      analytics.idv_final(
        success: true,
        fraud_review_pending: idv_session.profile.fraud_review_pending?,
        fraud_rejection: idv_session.profile.fraud_rejection?,
        fraud_pending_reason: idv_session.profile.fraud_pending_reason,
        gpo_verification_pending: idv_session.profile.gpo_verification_pending?,
        in_person_verification_pending: idv_session.profile.in_person_verification_pending?,
        deactivation_reason: idv_session.profile.deactivation_reason,
        proofing_workflow_time_in_seconds: idv_session.proofing_workflow_time_in_seconds,
        **ab_test_analytics_buckets,
      )

      return unless FeatureManagement.reveal_gpo_code?
      session[:last_gpo_confirmation_code] = idv_session.gpo_otp
    end

    def step_indicator_step
      return :re_enter_password unless idv_session.verify_by_mail?
      :verify_address
    end

    def self.step_info
      Idv::StepInfo.new(
        key: :enter_password,
        controller: self,
        action: :new,
        next_steps: [:personal_key],
        preconditions: ->(idv_session:, user:) do
          idv_session.phone_or_address_step_complete?
        end,
        undo_step: ->(idv_session:, user:) {},
      )
    end

    private

    def title
      idv_session.verify_by_mail? ?
        t('titles.idv.enter_password_letter')
        : t('titles.idv.enter_password')
    end

    def heading
      if idv_session.verify_by_mail?
        t('idv.titles.session.enter_password_letter', app_name: APP_NAME)
      else
        t('idv.titles.session.enter_password', app_name: APP_NAME)
      end
    end

    def confirm_current_password
      return if valid_password?

      analytics.idv_enter_password_submitted(
        success: false,
        gpo_verification_pending: current_user.gpo_verification_pending_profile?,
        # note: this always returns false as of 8/23
        in_person_verification_pending: current_user.in_person_pending_profile?,
        fraud_review_pending: fraud_review_pending?,
        fraud_rejection: fraud_rejection?,
        fraud_pending_reason: nil,
        **ab_test_analytics_buckets,
      )

      flash[:error] = t('idv.errors.incorrect_password')
      redirect_to idv_enter_password_url
    end

    def init_profile
      reproof = current_user.has_proofed_before?
      profile = idv_session.create_profile_from_applicant_with_password(
        password,
        is_enhanced_ipp: resolved_authn_context_result.enhanced_ipp?,
        proofing_components: ProofingComponents.new(idv_session:).to_h,
      )

      if profile.gpo_verification_pending?
        current_user.send_email_to_all_addresses(:verify_by_mail_letter_requested)
        log_letter_enqueued_analytics(resend: false)
      end

      if profile.fraud_review_pending? && !profile.in_person_verification_pending?
        current_user.send_email_to_all_addresses(:idv_please_call)
      end

      if profile.active?
        create_user_event(:account_verified)
        UserAlerts::AlertUserAboutAccountVerified.call(
          profile: idv_session.profile,
        )
        attempts_api_tracker.idv_enrollment_complete(reproof:)
        fraud_ops_tracker.idv_enrollment_complete(reproof:)
      end
    end

    def first_letter_requested_at
      idv_session.profile.gpo_verification_pending_at
    end

    def valid_password?
      current_user.valid_password?(password)
    end

    def password
      params.fetch(:user, {})[:password].presence
    end

    def confirm_no_profile_yet
      # When no profile has been minted yet, keep them on this page.
      return if !idv_session.profile.present?

      # If the user is in the IPP flow, but we haven't actually managed to
      # set up their enrollment (due to exception), allow them to
      # see this page so they can re-submit and attempt to establish the
      # enrollment.
      is_ipp_and_needs_to_enroll_with_usps =
        idv_session.profile.in_person_verification_pending? &&
        idv_session.profile.in_person_enrollment&.establishing?

      return if is_ipp_and_needs_to_enroll_with_usps

      # Otherwise, move the user on
      redirect_to next_step
    end

    def next_step
      if idv_session.verify_by_mail?
        idv_letter_enqueued_url
      else
        idv_personal_key_url
      end
    end

    def handle_request_enroll_exception(err)
      analytics.idv_in_person_usps_request_enroll_exception(
        context: context,
        enrollment_id: err.enrollment_id,
        exception_class: err.class.to_s,
        original_exception_class: err.exception_class,
        exception_message: scrub_message(err.message),
        reason: 'Request exception',
      )
      flash[:error] = t('idv.failure.exceptions.internal_error')
      idv_session.invalidate_personal_key!
      redirect_to idv_enter_password_url
    end
  end
end
