require 'rails_helper'

RSpec.describe 'Hybrid Flow' do
  include IdvHelper
  include IdvStepHelper
  include DocAuthHelper

  let(:user) { user_with_2fa }
  let(:phone_number) { Features::SessionHelper::IAL1_USER_PHONE }
  let(:fake_socure_document_capture_app_url) { 'https://verify.fake-socure.test/something' }
  let(:fake_socure_docv_document_request_endpoint) { 'https://fake-socure.test/document-request' }
  let(:fake_analytics) { FakeAnalytics.new }
  let(:socure_docv_webhook_repeat_endpoints) { [] }
  let(:timeout_socure_route) do
    idv_hybrid_mobile_socure_document_capture_errors_url(error_code: :timeout)
  end

  before do
    allow(IdentityConfig.store).to receive_messages(
      doc_auth_passport_selfie_vendor_lexis_nexis_percent: 0,
      doc_auth_passport_selfie_vendor_socure_percent: 100,
      doc_auth_passport_selfie_vendor_switching_enabled: true,
      doc_auth_passport_vendor_lexis_nexis_percent: 0,
      doc_auth_passport_vendor_socure_percent: 100,
      doc_auth_passport_vendor_switching_enabled: true,
      doc_auth_selfie_vendor_lexis_nexis_percent: 0,
      doc_auth_selfie_vendor_socure_percent: 100,
      doc_auth_selfie_vendor_switching_enabled: true,
      doc_auth_vendor_lexis_nexis_percent: 0,
      doc_auth_vendor_socure_percent: 100,
      doc_auth_vendor_switching_enabled: true,
      ruby_workers_idv_enabled: false,
      socure_docv_document_request_endpoint: fake_socure_docv_document_request_endpoint,
      socure_docv_enabled: true,
      socure_docv_webhook_repeat_endpoints:,
      use_vot_in_sp_requests: true,
    )
    socure_docv_webhook_repeat_endpoints.each { |endpoint| stub_request(:post, endpoint) }
    allow(FeatureManagement).to receive(:doc_capture_polling_enabled?).and_return(true)
    allow(Telephony).to receive(:send_doc_auth_link).and_wrap_original do |impl, config|
      @sms_link = config[:link]
      impl.call(**config)
    end.at_least(1).times
    @docv_transaction_token = stub_docv_document_request(user:)
    stub_analytics
    allow_any_instance_of(SocureDocvResultsJob).to receive(:analytics).and_return(@analytics)
    reload_ab_tests
  end

  context 'happy path', allow_browser_log: true do
    before do
      @docv_stub = stub_docv_verification_data_pass(
        docv_transaction_token: @docv_transaction_token,
        user:,
      )
    end

    it 'proofs and hands off to mobile', js: true do
      expect(SocureDocvRepeatWebhookJob).not_to receive(:perform_later)

      perform_in_browser(:desktop) do
        visit_idp_from_oidc_sp_with_ial2
        sign_in_and_2fa_user(user)

        complete_doc_auth_steps_before_hybrid_handoff_step
        clear_and_fill_in(:doc_auth_phone, phone_number)
        click_send_link

        expect(page).to have_content(t('doc_auth.headings.text_message'))
        expect(page).to have_content(t('doc_auth.info.you_entered'))
        expect(page).to have_content("+1 #{phone_number}")

        # Confirm that Continue button is not shown when polling is enabled
        expect(page).not_to have_content(t('doc_auth.buttons.continue'))
      end

      expect(@sms_link).to be_present

      perform_in_browser(:mobile) do
        visit @sms_link

        complete_choose_id_type_step
        # Confirm that jumping to LinkSent page does not cause errors
        visit idv_link_sent_url
        expect(page).to have_current_path(root_url)

        # Confirm that we end up on the Socure page even if we try to
        # go to the LN / Mock one.
        visit idv_hybrid_mobile_document_capture_url
        expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_url)

        # Confirm that clicking cancel and then coming back doesn't cause errors
        click_link 'Cancel'
        visit idv_hybrid_mobile_socure_document_capture_url

        # Confirm that jumping to Phone page does not cause errors
        visit idv_phone_url
        expect(page).to have_current_path(root_url)
        visit idv_hybrid_mobile_socure_document_capture_url

        # Confirm that jumping to Welcome page does not cause errors
        visit idv_welcome_url
        expect(page).to have_current_path(root_url)
        visit idv_hybrid_mobile_socure_document_capture_url

        expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_url)
        click_idv_continue
        expect(page).to have_current_path(fake_socure_document_capture_app_url)
        socure_docv_upload_documents(docv_transaction_token: @docv_transaction_token)
        visit idv_hybrid_mobile_socure_document_capture_update_url

        expect(page).to have_current_path(idv_hybrid_mobile_capture_complete_url)
        expect(page).to have_content(strip_nbsp(t('doc_auth.headings.capture_complete')))
        expect(page).to have_text(t('doc_auth.instructions.switch_back'))
        expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_id'))

        # Confirm app disallows jumping back to DocumentCapture page
        visit idv_hybrid_mobile_socure_document_capture_url
        expect(page).to have_current_path(idv_hybrid_mobile_capture_complete_url)
        visit idv_hybrid_mobile_socure_document_capture_update_url
        expect(page).to have_current_path(idv_hybrid_mobile_capture_complete_url)
      end

      perform_in_browser(:desktop) do
        expect(page).to_not have_content(t('doc_auth.headings.text_message'), wait: 10)
        expect(page).to have_current_path(idv_ssn_path)
        expect(@analytics).to have_logged_event(:idv_socure_document_request_submitted)
        expect(@analytics).to have_logged_event(:idv_socure_verification_data_requested)
        expect(@analytics).to have_logged_event(
          'IdV: doc auth image upload vendor pii validation',
        )
        fill_out_ssn_form_ok
        click_idv_continue

        expect(page).to have_content(t('headings.verify'))
        complete_verify_step

        prefilled_phone = page.find(id: 'idv_phone_form_phone').value

        expect(
          PhoneFormatter.format(prefilled_phone),
        ).to eq(
          PhoneFormatter.format(user.default_phone_configuration.phone),
        )

        fill_out_phone_form_ok
        verify_phone_otp

        fill_in t('idv.form.password'), with: Features::SessionHelper::VALID_PASSWORD
        click_idv_continue

        acknowledge_and_confirm_personal_key

        validate_idv_completed_page(user)
        click_agree_and_continue

        validate_return_to_sp
      end
    end

    context 'when a passport is submitted' do
      before do
        allow(IdentityConfig.store).to receive(:doc_auth_passports_enabled).and_return(true)
        stub_request(:get, IdentityConfig.store.dos_passport_composite_healthcheck_endpoint)
          .to_return_json({ status: 200, body: { status: 'UP' } })
        DocAuth::Mock::DocAuthMockClient.reset!
        stub_docv_verification_data_pass(
          docv_transaction_token: @docv_transaction_token,
          reason_codes: ['not_processed'],
          document_type: :passport,
          user:,
        )
        allow(IdentityConfig.store).to receive(:dos_passport_mrz_endpoint)
          .and_return('https://fake-socure.test/mrz')
      end

      it 'proofs and hands off to mobile', js: true do
        expect(SocureDocvRepeatWebhookJob).not_to receive(:perform_later)

        perform_in_browser(:desktop) do
          visit_idp_from_oidc_sp_with_ial2
          sign_in_and_2fa_user(user)

          complete_doc_auth_steps_before_hybrid_handoff_step
          clear_and_fill_in(:doc_auth_phone, phone_number)
          click_send_link
        end

        expect(@sms_link).to be_present

        perform_in_browser(:mobile) do
          visit @sms_link

          expect(page).to have_current_path(idv_hybrid_mobile_choose_id_type_url)
          choose(t('doc_auth.forms.id_type_preference.passport'))
          click_on t('forms.buttons.continue')

          expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_url)

          @mrz_stub = stub_request(:post, IdentityConfig.store.dos_passport_mrz_endpoint)
            .to_return_json({ status: 200, body: { response: 'NO' } })

          click_idv_continue
          expect(page).to have_current_path(fake_socure_document_capture_app_url)
          socure_docv_upload_documents(docv_transaction_token: @docv_transaction_token)
          visit idv_hybrid_mobile_socure_document_capture_update_url

          expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_errors_url)
          expect(page).to have_content(t('idv.errors.try_again_later'))
          expect(page).to have_content(
            I18n.t('idv.troubleshooting.options.use_another_id_type'),
          )

          click_on t('idv.failure.button.warning')

          remove_request_stub(@mrz_stub)

          expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_url)
          expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_id'))

          stub_request(:post, IdentityConfig.store.dos_passport_mrz_endpoint)
            .to_return_json({ status: 200, body: { response: 'YES' } })
          click_idv_continue
          socure_docv_upload_documents(
            docv_transaction_token: @docv_transaction_token,
          )
          visit idv_hybrid_mobile_socure_document_capture_update_url

          expect(page).to have_current_path(idv_hybrid_mobile_capture_complete_url)
          expect(page).to have_content(strip_nbsp(t('doc_auth.headings.capture_complete')))
          expect(page).to have_text(t('doc_auth.instructions.switch_back'))
          expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_id'))
        end

        perform_in_browser(:desktop) do
          expect(page).to_not have_content(t('doc_auth.headings.text_message'), wait: 10)
          expect(page).to have_current_path(idv_ssn_path)
          expect(@analytics).to have_logged_event(:idv_socure_document_request_submitted)
          expect(@analytics).to have_logged_event(:idv_socure_verification_data_requested)
          expect(@analytics).to have_logged_event(
            'IdV: doc auth image upload vendor pii validation',
          )
          fill_out_ssn_form_ok
          click_idv_continue
        end
      end

      context 'when a selfie is required', :js do
        before do
          allow(IdentityConfig.store).to receive_messages(
            doc_auth_max_attempts: 4,
            doc_auth_socure_wait_polling_timeout_minutes: 0,
            idv_socure_reason_codes_docv_selfie_fail: ['fail'],
            idv_socure_reason_codes_docv_selfie_not_processed: ['not_processed'],
            idv_socure_reason_codes_docv_selfie_pass: ['pass'],
            use_vot_in_sp_requests: true,
          )
          stub_request(:post, IdentityConfig.store.dos_passport_mrz_endpoint)
            .to_return_json({ status: 200, body: { response: 'YES' } })
        end

        it 'proceeds to the next page with valid info' do
          expect(SocureDocvRepeatWebhookJob).not_to receive(:perform_later)

          perform_in_browser(:desktop) do
            visit_idp_from_oidc_sp_with_ial2(facial_match_required: true)
            sign_in_and_2fa_user(user)

            complete_doc_auth_steps_before_hybrid_handoff_step
            clear_and_fill_in(:doc_auth_phone, phone_number)
            click_send_link
          end

          expect(@sms_link).to be_present

          perform_in_browser(:mobile) do
            visit @sms_link

            expect(page).to have_current_path(idv_hybrid_mobile_choose_id_type_url)
            choose(t('doc_auth.forms.id_type_preference.passport'))
            click_on t('forms.buttons.continue')

            expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_url)
            expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_id'))

            remove_request_stub(@docv_stub)
            @docv_stub = stub_docv_verification_data_pass(
              docv_transaction_token: @docv_transaction_token,
              reason_codes: ['not_processed'],
              user:,
              document_type: :passport,
            )

            socure_docv_upload_documents(
              docv_transaction_token: @docv_transaction_token,
              webhooks: selfie_webhook_list,
            )

            visit idv_hybrid_mobile_socure_document_capture_update_url
            expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_errors_url)
            expect(page).to have_content(t('idv.errors.try_again_later'))

            click_on t('idv.failure.button.warning')

            remove_request_stub(@docv_stub)
            @docv_stub = stub_docv_verification_data_pass(
              docv_transaction_token: @docv_transaction_token,
              reason_codes: ['fail'],
              user:,
              document_type: :passport,
            )

            click_idv_continue
            socure_docv_upload_documents(
              docv_transaction_token: @docv_transaction_token,
              webhooks: selfie_webhook_list,
            )

            visit idv_hybrid_mobile_socure_document_capture_update_url
            expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_errors_url)

            expect(page).to have_content(t('doc_auth.errors.selfie_fail_heading'))

            click_on t('idv.failure.button.warning')

            remove_request_stub(@docv_stub)
            @docv_stub = stub_docv_verification_data_fail_with(
              docv_transaction_token: @docv_transaction_token,
              reason_codes: ['pass'],
              user:,
              document_type: :passport,
            )

            click_idv_continue
            socure_docv_upload_documents(
              docv_transaction_token: @docv_transaction_token,
              webhooks: selfie_webhook_list,
            )

            visit idv_hybrid_mobile_socure_document_capture_update_url
            expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_errors_url)
            expect(page).to have_content(t('doc_auth.headers.unreadable_id'))

            click_on t('idv.failure.button.warning')

            remove_request_stub(@docv_stub)
            @docv_stub = stub_docv_verification_data_pass(
              docv_transaction_token: @docv_transaction_token,
              reason_codes: ['pass'],
              user:,
              document_type: :passport,
            )

            click_idv_continue
            socure_docv_upload_documents(
              docv_transaction_token: @docv_transaction_token,
              webhooks: selfie_webhook_list,
            )

            visit idv_hybrid_mobile_socure_document_capture_update_url

            expect(page).to have_current_path(idv_hybrid_mobile_capture_complete_url)
            expect(page).to have_content(strip_nbsp(t('doc_auth.headings.capture_complete')))
            expect(page).to have_text(t('doc_auth.instructions.switch_back'))
            expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_id'))
          end

          perform_in_browser(:desktop) do
            expect(page).to_not have_content(t('doc_auth.headings.text_message'), wait: 10)
            expect(page).to have_current_path(idv_ssn_path)
            expect(@analytics).to have_logged_event(:idv_socure_document_request_submitted)
            expect(@analytics).to have_logged_event(:idv_socure_verification_data_requested)
            expect(@analytics).to have_logged_event(
              'IdV: doc auth image upload vendor pii validation',
            )
            fill_out_ssn_form_ok
            click_idv_continue
          end
        end
      end
    end

    it 'shows the waiting screen correctly after cancelling from mobile and restarting', js: true do
      expect(SocureDocvRepeatWebhookJob).not_to receive(:perform_later)

      perform_in_browser(:desktop) do
        sign_in_and_2fa_user(user)
        complete_doc_auth_steps_before_hybrid_handoff_step
        clear_and_fill_in(:doc_auth_phone, phone_number)
        click_send_link

        expect(page).to have_content(t('doc_auth.headings.text_message'))
      end

      expect(@sms_link).to be_present

      perform_in_browser(:mobile) do
        visit @sms_link
        complete_choose_id_type_step
        expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_url)
        expect(page).not_to have_content(t('doc_auth.headings.document_capture_selfie'))
        click_on t('links.cancel')
        click_on t('forms.buttons.cancel') # Yes, cancel
      end

      perform_in_browser(:desktop) do
        expect(page).to_not have_content(t('doc_auth.headings.text_message'), wait: 10)
        clear_and_fill_in(:doc_auth_phone, phone_number)
        click_send_link

        expect(page).to have_content(t('doc_auth.headings.text_message'))
      end
    end

    context 'user times out waiting for result' do
      before do
        DocAuth::Mock::DocAuthMockClient.reset!
        allow(IdentityConfig.store).to receive_messages(
          doc_auth_passports_enabled: false,
          doc_auth_socure_wait_polling_timeout_minutes: 0,
          in_person_doc_auth_button_enabled: true,
          in_person_proofing_enabled: true,
        )
        allow(Idv::InPersonConfig).to receive(:enabled_for_issuer?).and_return(true)
        allow_any_instance_of(DocumentCaptureSession).to receive(:load_result).and_return(nil)
      end

      it 'presents options to try again or try in person', js: true do
        perform_in_browser(:desktop) do
          visit_idp_from_oidc_sp_with_ial2
          sign_in_and_2fa_user(user)

          complete_doc_auth_steps_before_hybrid_handoff_step
          clear_and_fill_in(:doc_auth_phone, phone_number)
          click_send_link
        end

        perform_in_browser(:mobile) do
          visit @sms_link
          complete_choose_id_type_step
          expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_url)

          click_idv_continue
          expect(page).to have_current_path(fake_socure_document_capture_app_url)

          # Fail to receive the DOCUMENTS_UPLOADED and SESSION_COMPLETE webhooks
          socure_docv_upload_documents(
            docv_transaction_token: @docv_transaction_token,
            webhooks: %w[
              WAITING_FOR_USER_TO_REDIRECT,
              APP_OPENED,
              DOCUMENT_FRONT_UPLOADED,
              DOCUMENT_BACK_UPLOADED,
            ],
          )

          # Go to the wait page
          visit idv_hybrid_mobile_socure_document_capture_update_url
          expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_update_path)

          # Timeout
          visit idv_hybrid_mobile_socure_document_capture_update_url
          expect(page).to have_current_path(timeout_socure_route)
          expect(page).to have_content(I18n.t('idv.errors.try_again_later'))
          expect(page).to have_content(
            I18n.t('idv.troubleshooting.options.use_another_id_type'),
          )

          # Try in person
          click_on t('in_person_proofing.body.cta.button')
          expect(page).to have_current_path(
            idv_hybrid_mobile_document_capture_path(step: :idv_doc_auth),
          )
          expect(page).to have_content(t('in_person_proofing.headings.prepare'))

          # Go back
          click_on t('forms.buttons.back')
          expect(page).to have_current_path(timeout_socure_route)

          # Try Socure again
          click_on t('idv.failure.button.warning')
          expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_path)
          expect(page).to have_content(t('doc_auth.headings.document_capture'))

          # Correct intertitial with passport content
          visit idv_hybrid_mobile_socure_document_capture_update_url
          expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_update_url)

          # Correct intertitial with passport content
          visit idv_hybrid_mobile_socure_document_capture_update_url
          document_capture_session = DocumentCaptureSession.find_by(user_id: user.id)
          document_capture_session.update(passport_status: 'requested')
          document_capture_session.save!
          click_on t('idv.failure.button.warning')
          expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_path)
          expect(page).to have_content(t('doc_auth.headings.passport_capture'))
          expect(page).to have_content(t('doc_auth.info.socure_passport', app_name: APP_NAME))
        end
      end
    end

    context 'user is rate limited on mobile' do
      let(:max_attempts) { IdentityConfig.store.doc_auth_max_attempts }
      let(:socure_docv_webhook_repeat_endpoints) do # repeat webhooks
        ['https://1.example.test/thepath', 'https://2.example.test/thepath']
      end

      before do
        remove_request_stub(@docv_stub)
        stub_docv_verification_data_fail_with(
          docv_transaction_token: @docv_transaction_token,
          reason_codes: ['R827'],
          user:,
        )
      end

      it 'shows capture complete on mobile and error page on desktop', js: true do
        expect(SocureDocvRepeatWebhookJob).to receive(:perform_later)
          .exactly(6 * max_attempts * socure_docv_webhook_repeat_endpoints.length)
          .times.and_call_original

        perform_in_browser(:desktop) do
          sign_in_and_2fa_user(user)
          complete_doc_auth_steps_before_hybrid_handoff_step
          clear_and_fill_in(:doc_auth_phone, phone_number)
          click_send_link

          expect(page).to have_content(t('doc_auth.headings.text_message'))
        end

        expect(@sms_link).to be_present

        perform_in_browser(:mobile) do
          visit @sms_link
          complete_choose_id_type_step
          click_idv_continue
          expect(page).to have_current_path(fake_socure_document_capture_app_url)
          max_attempts.times do
            socure_docv_upload_documents(docv_transaction_token: @docv_transaction_token)
          end

          visit idv_hybrid_mobile_socure_document_capture_update_url
          expect(page).to have_current_path(idv_hybrid_mobile_capture_complete_url)
          expect(page).to have_text(t('doc_auth.instructions.switch_back'))

          visit idv_hybrid_mobile_socure_document_capture_url
          expect(page).to have_current_path(idv_hybrid_mobile_capture_complete_url)
          expect(page).to have_text(t('doc_auth.instructions.switch_back'))
        end

        perform_in_browser(:desktop) do
          expect(page).to have_current_path(idv_session_errors_rate_limited_path, wait: 10)
        end
      end
    end

    context 'user reaches rate limit and succeeds on last request' do
      let(:max_attempts) { IdentityConfig.store.doc_auth_max_attempts }
      let(:socure_docv_webhook_repeat_endpoints) do # repeat webhooks
        ['https://1.example.test/thepath', 'https://2.example.test/thepath']
      end

      before do
        stub_request(:post, "#{IdentityConfig.store.socure_idplus_base_url}/api/3.0/EmailAuthScore")
          .to_return(
            headers: {
              'Content-Type' => 'application/json',
            },
            body: SocureDocvFixtures.fail_json(reason_codes: ['R287']),
          )
          .times(max_attempts - 1)
          .then
          .to_return do |_request|
            sleep(1)
            {
              headers: {
                'Content-Type' => 'application/json',
              },
              body: SocureDocvFixtures.pass_json,
            }
          end
      end

      it 'shows capture complete on mobile and navigates to SSN on desktop', js: true do
        expect(SocureDocvRepeatWebhookJob).to receive(:perform_later)
          .exactly(6 * max_attempts * socure_docv_webhook_repeat_endpoints.length)
          .times.and_call_original

        perform_in_browser(:desktop) do
          sign_in_and_2fa_user(user)
          complete_doc_auth_steps_before_hybrid_handoff_step
          clear_and_fill_in(:doc_auth_phone, phone_number)
          click_send_link

          expect(page).to have_content(t('doc_auth.headings.text_message'))
        end

        expect(@sms_link).to be_present

        perform_in_browser(:mobile) do
          visit @sms_link
          complete_choose_id_type_step
          click_idv_continue
          expect(page).to have_current_path(fake_socure_document_capture_app_url)
          max_attempts.times do
            socure_docv_upload_documents(docv_transaction_token: @docv_transaction_token)
          end

          visit idv_hybrid_mobile_socure_document_capture_update_url
          expect(page).to have_current_path(idv_hybrid_mobile_capture_complete_url)
          expect(page).to have_text(t('doc_auth.instructions.switch_back'))

          visit idv_hybrid_mobile_socure_document_capture_url
          expect(page).to have_current_path(idv_hybrid_mobile_capture_complete_url)
          expect(page).to have_text(t('doc_auth.instructions.switch_back'))
        end

        perform_in_browser(:desktop) do
          expect(page).to have_current_path(idv_ssn_path, wait: 10)
        end
      end
    end

    context 'if the user has no MFA phone' do
      let(:user) { create(:user, :with_authentication_app) }
      let(:socure_docv_webhook_repeat_endpoints) do # repeat webhooks
        ['https://1.example.test/thepath', 'https://2.example.test/thepath']
      end

      before do
        # recovers when fails to repeat webhook to an endpoint
        allow_any_instance_of(DocAuth::Socure::WebhookRepeater)
          .to receive(:send_http_post_request).and_raise('doh')
      end

      it 'prefills the phone number used on the phone step', :js do
        expect(SocureDocvRepeatWebhookJob).to receive(:perform_later)
          .exactly(6 * socure_docv_webhook_repeat_endpoints.length)
          .times.and_call_original

        perform_in_browser(:desktop) do
          visit_idp_from_oidc_sp_with_ial2
          sign_in_and_2fa_user(user, auth_method: 'totp')

          complete_doc_auth_steps_before_hybrid_handoff_step
          clear_and_fill_in(:doc_auth_phone, phone_number)
          click_send_link
        end

        expect(@sms_link).to be_present

        perform_in_browser(:mobile) do
          visit @sms_link
          complete_choose_id_type_step
          expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_url)
          click_idv_continue
          expect(page).to have_current_path(fake_socure_document_capture_app_url)
          socure_docv_upload_documents(docv_transaction_token: @docv_transaction_token)
          visit idv_hybrid_mobile_socure_document_capture_update_url

          expect(page).to have_current_path(idv_hybrid_mobile_capture_complete_url)
          expect(page).to have_text(t('doc_auth.instructions.switch_back'))
        end

        perform_in_browser(:desktop) do
          expect(page).to have_current_path(idv_ssn_path, wait: 10)

          fill_out_ssn_form_ok
          click_idv_continue

          expect(page).to have_content(t('headings.verify'))
          complete_verify_step

          prefilled_phone = page.find(id: 'idv_phone_form_phone').value

          expect(
            PhoneFormatter.format(prefilled_phone),
          ).to eq(
            PhoneFormatter.format(phone_number),
          )
        end
      end
    end

    context 'invalid ID type' do
      it 'presents as an unaccepted ID type error', js: true do
        perform_in_browser(:desktop) do
          visit_idp_from_oidc_sp_with_ial2
          sign_in_and_2fa_user(user)

          complete_doc_auth_steps_before_hybrid_handoff_step
          clear_and_fill_in(:doc_auth_phone, phone_number)
          click_send_link

          expect(page).to have_content(t('doc_auth.headings.text_message'))
          expect(page).to have_content(t('doc_auth.info.you_entered'))
          expect(page).to have_content("+1 #{phone_number}")

          # Confirm that Continue button is not shown when polling is enabled
          expect(page).not_to have_content(t('doc_auth.buttons.continue'))
        end

        expect(@sms_link).to be_present

        perform_in_browser(:mobile) do
          visit @sms_link
          complete_choose_id_type_step
          body = JSON.parse(SocureDocvFixtures.pass_json)
          body['documentVerification']['documentType']['type'] = 'Non-Document-Type'
          remove_request_stub(@docv_stub)
          stub_docv_verification_data(
            docv_transaction_token: @docv_transaction_token,
            body: body.to_json,
            user:,
          )

          click_idv_continue

          socure_docv_upload_documents(docv_transaction_token: @docv_transaction_token)

          visit idv_hybrid_mobile_socure_document_capture_update_url

          expect(page).to have_content(t('doc_auth.headers.unaccepted_id_type'))
          expect(page).to have_content(t('doc_auth.errors.unaccepted_id_type'))
        end

        perform_in_browser(:desktop) do
          expect(page).to have_current_path(idv_link_sent_path)
        end
      end
    end

    context 'selfie is required' do
      before do
        allow(IdentityConfig.store).to receive_messages(
          doc_auth_max_attempts: 6,
          doc_auth_socure_wait_polling_timeout_minutes: 0,
          idv_socure_reason_codes_docv_selfie_fail: ['fail'],
          idv_socure_reason_codes_docv_selfie_not_processed: ['not_processed'],
          idv_socure_reason_codes_docv_selfie_pass: ['pass'],
          use_vot_in_sp_requests: true,
        )
      end

      it 'proofs and hands off to mobile', js: true do
        expect(SocureDocvRepeatWebhookJob).not_to receive(:perform_later)

        perform_in_browser(:desktop) do
          visit_idp_from_oidc_sp_with_ial2(facial_match_required: true)
          sign_in_and_2fa_user(user)

          complete_doc_auth_steps_before_hybrid_handoff_step
          clear_and_fill_in(:doc_auth_phone, phone_number)
          click_send_link

          expect(page).to have_content(t('doc_auth.headings.text_message'))
          expect(page).to have_content(t('doc_auth.info.you_entered'))
          expect(page).to have_content("+1 #{phone_number}")

          # Confirm that Continue button is not shown when polling is enabled
          expect(page).not_to have_content(t('doc_auth.buttons.continue'))
        end

        expect(@sms_link).to be_present

        perform_in_browser(:mobile) do
          visit @sms_link
          complete_choose_id_type_step
          click_idv_continue
          expect(page).to have_current_path(fake_socure_document_capture_app_url)
          socure_docv_upload_documents(docv_transaction_token: @docv_transaction_token)
          visit idv_hybrid_mobile_socure_document_capture_update_url

          expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_errors_url)
          expect(page).to have_content(t('idv.errors.try_again_later'))

          click_on t('idv.failure.button.warning')

          remove_request_stub(@docv_stub)
          @docv_stub = stub_docv_verification_data_pass(
            docv_transaction_token: @docv_transaction_token,
            reason_codes: ['not_processed'],
            user:,
          )

          click_idv_continue
          expect(page).to have_current_path(fake_socure_document_capture_app_url)
          socure_docv_upload_documents(
            docv_transaction_token: @docv_transaction_token,
            webhooks: selfie_webhook_list,
          )
          visit idv_hybrid_mobile_socure_document_capture_update_url

          expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_errors_url)
          expect(page).to have_content(t('idv.errors.try_again_later'))

          click_on t('idv.failure.button.warning')
          remove_request_stub(@docv_stub)
          @docv_stub = stub_docv_verification_data_fail_with(
            docv_transaction_token: @docv_transaction_token,
            reason_codes: ['pass'],
            user:,
          )

          click_idv_continue
          expect(page).to have_current_path(fake_socure_document_capture_app_url)
          socure_docv_upload_documents(
            docv_transaction_token: @docv_transaction_token,
            webhooks: selfie_webhook_list,
          )
          visit idv_hybrid_mobile_socure_document_capture_update_url

          expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_errors_url)
          expect(page).to have_content(t('doc_auth.headers.unreadable_id'))

          click_on t('idv.failure.button.warning')

          remove_request_stub(@docv_stub)
          @docv_stub = stub_docv_verification_data_pass(
            docv_transaction_token: @docv_transaction_token,
            reason_codes: ['pass'],
            user:,
          )

          click_idv_continue
          socure_docv_upload_documents(
            docv_transaction_token: @docv_transaction_token,
            webhooks: selfie_webhook_list,
          )

          visit idv_hybrid_mobile_socure_document_capture_update_path

          expect(page).to have_content(strip_nbsp(t('doc_auth.headings.capture_complete')))
          expect(page).to have_text(t('doc_auth.instructions.switch_back'))
          expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_id'))

          # Confirm app disallows jumping back to DocumentCapture page
          visit idv_hybrid_mobile_socure_document_capture_url
          expect(page).to have_current_path(idv_hybrid_mobile_capture_complete_url)
          visit idv_hybrid_mobile_socure_document_capture_update_url
          expect(page).to have_current_path(idv_hybrid_mobile_capture_complete_url)
        end

        perform_in_browser(:desktop) do
          expect(page).to_not have_content(t('doc_auth.headings.text_message'), wait: 10)
          expect(page).to have_current_path(idv_ssn_path)
          expect(@analytics).to have_logged_event(:idv_socure_document_request_submitted)
          expect(@analytics).to have_logged_event(:idv_socure_verification_data_requested)
          expect(@analytics).to have_logged_event(
            'IdV: doc auth image upload vendor pii validation',
          )
          fill_out_ssn_form_ok
          click_idv_continue

          expect(page).to have_content(t('headings.verify'))
          complete_verify_step

          prefilled_phone = page.find(id: 'idv_phone_form_phone').value

          expect(
            PhoneFormatter.format(prefilled_phone),
          ).to eq(
            PhoneFormatter.format(user.default_phone_configuration.phone),
          )

          fill_out_phone_form_ok
          verify_phone_otp

          fill_in t('idv.form.password'), with: Features::SessionHelper::VALID_PASSWORD
          click_idv_continue

          acknowledge_and_confirm_personal_key

          validate_idv_completed_page(user)
          click_agree_and_continue

          validate_return_to_sp
        end
      end
    end
  end

  shared_examples 'a properly categorized Socure error' do |socure_error_code, expected_header_key|
    it 'shows the correct error page', allow_browser_log: true, js: true do
      perform_in_browser(:desktop) do
        visit_idp_from_oidc_sp_with_ial2
        sign_in_and_2fa_user(user)

        complete_doc_auth_steps_before_hybrid_handoff_step
        clear_and_fill_in(:doc_auth_phone, phone_number)
        click_send_link

        expect(page).to have_content(t('doc_auth.headings.text_message'))
        expect(page).to have_content(t('doc_auth.info.you_entered'))
        expect(page).to have_content("+1 #{phone_number}")

        # Confirm that Continue button is not shown when polling is enabled
        expect(page).not_to have_content(t('doc_auth.buttons.continue'))
      end

      expect(@sms_link).to be_present

      perform_in_browser(:mobile) do
        visit @sms_link
        complete_choose_id_type_step
        stub_docv_verification_data_fail_with(
          docv_transaction_token: @docv_transaction_token,
          reason_codes: [socure_error_code],
          user:,
        )

        click_idv_continue

        socure_docv_upload_documents(docv_transaction_token: @docv_transaction_token)
        visit idv_hybrid_mobile_socure_document_capture_update_url

        expect(page).to have_text(t(expected_header_key))

        click_try_again

        expect(page).to have_current_path(idv_hybrid_mobile_socure_document_capture_path)
      end

      perform_in_browser(:desktop) do
        expect(page).to have_current_path(idv_link_sent_path)
      end
    end
  end

  context 'a type 1 error (because we do not recognize the code)' do
    it_behaves_like 'a properly categorized Socure error', 'XXXX', 'doc_auth.headers.unreadable_id'
  end

  context 'a type 1 error' do
    it_behaves_like 'a properly categorized Socure error', 'I848', 'doc_auth.headers.unreadable_id'
  end

  context 'a type 2 error' do
    it_behaves_like 'a properly categorized Socure error',
                    'I849',
                    'doc_auth.headers.unaccepted_id_type'
  end

  context 'a type 3 error' do
    it_behaves_like 'a properly categorized Socure error', 'R827', 'doc_auth.headers.expired_id'
  end

  context 'a type 4 error' do
    it_behaves_like 'a properly categorized Socure error', 'I808', 'doc_auth.headers.low_resolution'
  end

  context 'a type 5 error' do
    it_behaves_like 'a properly categorized Socure error', 'R845', 'doc_auth.headers.underage'
  end

  context 'a type 6 error' do
    it_behaves_like 'a properly categorized Socure error', 'I856', 'doc_auth.headers.id_not_found'
  end

  context 'with a network error requesting the capture app url' do
    shared_examples 'document request API failure' do
      it 'shows the network error page on the phone and the link sent page on the desktop',
         js: true do
        perform_in_browser(:desktop) do
          visit_idp_from_oidc_sp_with_ial2
          sign_in_and_2fa_user(user)

          complete_doc_auth_steps_before_hybrid_handoff_step
          clear_and_fill_in(:doc_auth_phone, phone_number)
          click_send_link
        end

        perform_in_browser(:mobile) do
          visit @sms_link
          complete_choose_id_type_step
          expect(page).to have_text(t('doc_auth.headers.general.network_error'))
          expect(page).to have_text(t('doc_auth.errors.general.new_network_error'))
          expect(@analytics).to have_logged_event(:idv_socure_document_request_submitted)
          expect(@analytics).not_to have_logged_event(
            'IdV: doc auth image upload vendor pii validation',
          )
        end

        perform_in_browser(:desktop) do
          expect(page).to have_current_path(idv_link_sent_path)
        end
      end
    end

    context 'Faraday connection error' do
      before do
        allow_any_instance_of(Faraday::Connection).to receive(:post)
          .and_raise(Faraday::ConnectionFailed)
      end

      it_behaves_like 'document request API failure'
    end

    context 'invalid request (ie: wrong api key)', allow_browser_log: true do
      before do
        DocAuth::Mock::DocAuthMockClient.reset!
        stub_docv_document_request(status: 401, user:)
      end

      it_behaves_like 'document request API failure'
    end

    context 'Pii validation fails' do
      before do
        allow_any_instance_of(Idv::DocPiiStateId).to receive(:zipcode).and_return(:invalid_junk)
      end

      it 'presents as a type 1 error', js: true do
        perform_in_browser(:desktop) do
          visit_idp_from_oidc_sp_with_ial2
          sign_in_and_2fa_user(user)

          complete_doc_auth_steps_before_hybrid_handoff_step
          clear_and_fill_in(:doc_auth_phone, phone_number)
          click_send_link

          expect(page).to have_content(t('doc_auth.headings.text_message'))
          expect(page).to have_content(t('doc_auth.info.you_entered'))
          expect(page).to have_content("+1 #{phone_number}")

          # Confirm that Continue button is not shown when polling is enabled
          expect(page).not_to have_content(t('doc_auth.buttons.continue'))
        end

        expect(@sms_link).to be_present

        perform_in_browser(:mobile) do
          visit @sms_link
          complete_choose_id_type_step
          stub_docv_verification_data_pass(docv_transaction_token: @docv_transaction_token, user:)

          click_idv_continue

          socure_docv_upload_documents(docv_transaction_token: @docv_transaction_token)
          visit idv_hybrid_mobile_socure_document_capture_update_url

          expect(page).to have_content(t('doc_auth.headers.unreadable_id'))
        end

        perform_in_browser(:desktop) do
          expect(page).to have_current_path(idv_link_sent_path)
        end
      end
    end
  end
end
