require 'rails_helper'

RSpec.describe SessionController do
  let(:email_token) { Fabricate(:email_token) }
  let(:user) { email_token.user }

  describe '#email_login' do
    before do
      SiteSetting.enable_local_logins_via_email = true
    end

    context 'missing token' do
      it 'returns the right response' do
        get "/session/email-login"
        expect(response.status).to eq(404)
      end
    end

    context 'invalid token' do
      it 'returns the right response' do
        get "/session/email-login/adasdad"

        expect(response).to be_success

        expect(CGI.unescapeHTML(response.body)).to match(
          I18n.t('email_login.invalid_token')
        )
      end

      context 'when token has expired' do
        it 'should return the right response' do
          email_token.update!(created_at: 999.years.ago)

          get "/session/email-login/#{email_token.token}"

          expect(response).to be_success

          expect(CGI.unescapeHTML(response.body)).to match(
            I18n.t('email_login.invalid_token')
          )
        end
      end
    end

    context 'valid token' do
      it 'returns success' do
        get "/session/email-login/#{email_token.token}"

        expect(response).to redirect_to("/")
      end

      it 'fails when local logins via email is disabled' do
        SiteSetting.enable_local_logins_via_email = false

        get "/session/email-login/#{email_token.token}"

        expect(response.status).to eq(404)
      end

      it 'fails when local logins is disabled' do
        SiteSetting.enable_local_logins = false

        get "/session/email-login/#{email_token.token}"

        expect(response.status).to eq(500)
      end

      it "doesn't log in the user when not approved" do
        SiteSetting.must_approve_users = true

        get "/session/email-login/#{email_token.token}"

        expect(response.status).to eq(200)

        expect(CGI.unescapeHTML(response.body)).to include(
          I18n.t("login.not_approved")
        )
      end

      context "when admin IP address is not valid" do
        before do
          Fabricate(:screened_ip_address,
            ip_address: "111.111.11.11",
            action_type: ScreenedIpAddress.actions[:allow_admin]
          )

          SiteSetting.use_admin_ip_whitelist = true
          user.update!(admin: true)
        end

        it 'returns the right response' do
          get "/session/email-login/#{email_token.token}"

          expect(response.status).to eq(200)

          expect(CGI.unescapeHTML(response.body)).to include(
            I18n.t("login.admin_not_allowed_from_ip_address", username: user.username)
          )
        end
      end

      context "when IP address is blocked" do
        let(:permitted_ip_address) { '111.234.23.11' }

        before do
          Fabricate(:screened_ip_address,
            ip_address: permitted_ip_address,
            action_type: ScreenedIpAddress.actions[:block]
          )
        end

        it 'returns the right response' do
          ActionDispatch::Request.any_instance.stubs(:remote_ip).returns(permitted_ip_address)

          get "/session/email-login/#{email_token.token}"

          expect(response.status).to eq(200)

          expect(CGI.unescapeHTML(response.body)).to include(
            I18n.t("login.not_allowed_from_ip_address", username: user.username)
          )
        end
      end

      it "fails when user is suspended" do
        user.update!(
          suspended_till: 2.days.from_now,
          suspended_at: Time.zone.now
        )

        get "/session/email-login/#{email_token.token}"

        expect(response.status).to eq(200)

        expect(CGI.unescapeHTML(response.body)).to include(I18n.t("login.suspended",
          date: I18n.l(user.suspended_till, format: :date_only)
        ))
      end

      context 'user has 2-factor logins' do
        let!(:user_second_factor) { Fabricate(:user_second_factor, user: user) }

        describe 'requires second factor' do
          it 'should return a second factor prompt' do
            get "/session/email-login/#{email_token.token}"

            expect(response.status).to eq(200)

            response_body = CGI.unescapeHTML(response.body)

            expect(response_body).to include(I18n.t(
              "login.second_factor_title"
            ))

            expect(response_body).to_not include(I18n.t(
              "login.invalid_second_factor_code"
            ))
          end
        end

        describe 'errors on incorrect 2-factor' do
          it 'does not log in with incorrect two factor' do
            post "/session/email-login/#{email_token.token}", params: { second_factor_token: "0000" }

            expect(response.status).to eq(200)

            expect(CGI.unescapeHTML(response.body)).to include(I18n.t(
              "login.invalid_second_factor_code"
            ))
          end
        end

        describe 'allows successful 2-factor' do
          it 'logs in correctly' do
            post "/session/email-login/#{email_token.token}", params: {
              second_factor_token: ROTP::TOTP.new(user_second_factor.data).now
            }

            expect(response).to redirect_to("/")
          end
        end
      end
    end
  end

  context 'logoff support' do
    it 'can log off users cleanly' do
      user = Fabricate(:user)
      sign_in(user)

      UserAuthToken.destroy_all

      # we need a route that will call current user
      post '/draft.json', params: {}
      expect(response.headers['Discourse-Logged-Out']).to eq("1")
    end
  end
end