require 'rails_helper'
require_relative './shared_contexts'
require 'history/constants'

RSpec.describe Twitch::UsersController do
  include_context 'mocked permissions'
  include_context 'with example twitch users'

  let(:twofa_info) do
    {
      user_id: user.id,
      authy_id: "12345",
      last_four_digits: "5555",
      country_code: "1",
      region_code: "US",
      created_at: Time.now.to_i,
      updated_at: Time.now.to_i,
    }
  end
  let(:mock_response) do
    {
      status: 200,
      body: twofa_info.to_json
    }
  end
  let(:data_stub) { true }
  let(:data) do
    lambda {
      stub_request(:get, "#{Settings.passport.endpoint}/users/#{user.id}/2fa/info")
        .to_return(mock_response)
    }
  end
  before(:each) do
    if data_stub
      data.call
    end
  end

  describe 'index' do
    let(:permission) { any_permission_in('users', 'twitch_user:view_details') }

    describe 'permissions' do
      let(:action) { :index }
      let(:data_stub) { false }

      context 'a user with appropriate permissions' do
        include_context 'an authorized user'
        include_context 'it creates any tracking call'

        before do
          expect(Twitch::User).to receive(:all).and_return([])
          get action
        end

        it_behaves_like 'it returns a success response'
      end

      context 'a user without appropriate permissions' do
        include_context 'an unauthorized user'

        before do
          get action
        end

        it_behaves_like 'it redirects the user and displays an error'
      end
    end
  end

  describe 'index.csv' do
    let(:action) { :index }
    let(:user_id) { 'my-user-id' }
    let(:user_login) { 'my-user-login' }
    let(:user_email) { 'my-user-email' }
    let(:user_remote_ip) { 'my-user-remote-ip' }
    let(:user_created_on) { 'my-user-created-on' }
    let(:user_last_login) { 'my-user-last-login' }
    let(:user) do
      Twitch::User.new(
        id: user_id,
        login: user_login,
        email: user_email,
        remote_ip: user_remote_ip,
        created_on: user_created_on,
        last_login: user_last_login
      )
    end
    let(:users) { [user] }
    let(:permission) { any_permission_in('users', 'users_dump', 'twitch_user:view_details') }

    context 'a user with appropriate permissions' do
      include_context 'an authorized user'

      before do
        expect(Twitch::User).to receive(:all).and_return(users)
        allow(Subscriptions::Token).to receive(:get_token_balance).with(nil).and_return(0)
        request.headers['accept'] = 'text/csv'
        get action
      end

      it_behaves_like 'it returns a success response'

      it 'sends the correct csv' do
        res = CSV.parse(response.body)
        expect(res.length).to eq(2)
        expect(res[0]).to eq(%w[id login email remote_ip created_on last_login status])
        expect(res[1]).to eq([user_id, user_login, user_email, user_remote_ip, user_created_on, user_last_login, 'Active'])
      end

      context 'with more than a page of users' do
        let(:users) {[user, user, user, user, user, user, user, user, user, user, user]}

        it 'contains all users in the csv' do
          res = CSV.parse(response.body)

          expect(res.length).to eq(12)
        end
      end
    end

    context 'a user without appropriate permissions' do
      let(:data_stub) { false }
      include_context 'an unauthorized user'

      before do
        get action
      end

      it_behaves_like 'it redirects the user and displays an error'
    end
  end

  describe 'show' do
    let(:permission) { any_permission_in('users', 'twitch_user:view_details', 'twitch_user:view_personal_information') }
    let(:user) { Twitch::User.new }
    before :each do
      allow(Subscriptions::Token).to receive(:get_token_balance).with(nil).and_return(0)
      allow(Rbac::Membership).to receive(:find_memberships)
        .with(anything)
        .and_return([{}, nil])
      allow(Sessions::Cookies).to receive(:list).with(user.id).and_return(nil)
      allow(SupportTools::AegisGateway).to receive(:report_clip_count).with(anything).and_return({ "count" => 2 })
    end

    context 'with a user that does not exist' do
      before do
        allow(Twitch::User).to receive(:find).with('foo').and_return(nil)
        allow(user).to receive(:blank?).and_return(true)
      end

      before do
        get :show, params: { id: 'foo' }
      end

      it_behaves_like 'it redirects the user and displays an error'
    end

    context 'with a user that exists' do
      before do
        allow(Twitch::User).to receive(:find).with('foo').and_return(user)
        allow(user).to receive(:blank?).and_return(false)
        allow(user).to receive(:dmca_strikes_received).and_return([])
        allow(Subscriptions::Token).to receive(:get_token_balance).with(nil).and_return(0)
        fake_response = PlatformConnections::ConnectionResponse.new(true, nil)
        fake_youtube_response = PlatformConnections::ConnectionResponse.new(
          true,
          {"youtube_id" => "UCAPMSRjyCYQAv26hU3uocbA"}
        )

        allow(PlatformConnections::Connection).to receive(:get_all).and_return(
          Connections::BLIZZARD => fake_response,
          Connections::LEAGUE => fake_response,
          Connections::YOUTUBE => fake_youtube_response
        )
        allow(admin_account).to receive(:permitted?).with('users').and_return(true)
        allow(admin_account).to receive(:permitted?).with('staff_admins_global_mods').and_return(true)
        allow(admin_account).to receive(:permitted?).with('manage_validated_non_partner').and_return(true)
      end

      shared_context 'a user authorized to show' do
        include_context 'an authorized user acting on a twitch user'

        before do
          get :show, params: { id: 'foo' }
        end

        it_behaves_like 'it returns a success response'
        it_behaves_like 'it correctly redacts attributes'

        it 'sets the user' do
          expect(@controller.view_assigns['user']).to eq(user)
        end

        it 'fetches all platform connections for a user' do
          expect(@controller.view_assigns['user_connections'][Connections::BLIZZARD]).to eq(true)
          expect(@controller.view_assigns['user_connections'][Connections::LEAGUE]).to eq(true)
        end

        it 'sets the platforms' do
          expect(@controller.view_assigns['platforms'].keys).to include(Connections::YOUTUBE)
          expect(@controller.view_assigns['platforms'].keys).to include(Connections::BLIZZARD)
          expect(@controller.view_assigns['platforms'].keys).to include(Connections::LEAGUE)
        end

        it 'sets the additional information for specific platforms' do
          expect(@controller.view_assigns['youtube_url']).to eq("https://www.youtube.com/channel/UCAPMSRjyCYQAv26hU3uocbA")
        end
      end

      context 'an authorized user with access to PII via legacy permissions' do
        let(:item) { user }
        let(:unauthorized_attributes) { [] }
        include_context 'a user authorized to show'
      end

      context 'an authorized user with access to PII via current permissions' do
        let(:item) { user }
        let(:permission) { any_permission_in('twitch_user:view_details', 'twitch_user:view_personal_information') }
        let(:unauthorized_permission) { any_permission_in('users') }
        let(:unauthorized_attributes) { [] }

        include_context 'a user authorized to show'
      end

      context 'an authorized user without access to PII' do
        let(:item) { user }
        let(:permission) { any_permission_in('twitch_user:view_details') }
        let(:unauthorized_permission) { any_permission_in('users', 'twitch_user:view_personal_information') }
        let(:unauthorized_attributes) do
          Twitch::UserPolicy::DATA_CLASSIFICATION_CUSTOMER
            .reject do |attribute|
              Twitch::UserPolicy::DATA_CLASSIFICATION_RESTRICTED.include?(attribute)
            end
        end

        include_context 'a user authorized to show'
      end

      context 'an authorized user with access to update_channel_properties' do
        let(:item) { user }
        let(:permission) { any_permission_in('twitch_user:view_details', 'twitch_user:update_channel_properties') }
        let(:unauthorized_permission) { any_permission_in('users', 'twitch_user:view_personal_information') }
        let(:unauthorized_attributes) do
          Twitch::UserPolicy::DATA_CLASSIFICATION_CUSTOMER
            .reject do |attribute|
              Twitch::UserPolicy::DATA_CLASSIFICATION_RESTRICTED.include?(attribute)
            end
        end

        include_context 'a user authorized to show'
      end

      context 'an admin without appropriate permissions' do
        include_context 'an unauthorized user acting on a twitch user'

        before do
          get :show, params: { id: 'foo' }
        end

        it_behaves_like 'it redirects the user and displays an error'
      end

      describe 'roles' do
        let(:admin_account) { BeefCake::User.new(ldap_login: 'cool_ldap_login', email: 'cool@twitch.tv') }
        include_context 'an authorized user acting on a twitch user'

        it 'shows the edit roles button when authorized' do
          expect(admin_account).to receive(:permitted?).with('staff_admins_global_mods').and_return(true)
          get :show, params: { id: 'foo' }
          expect(@controller.view_assigns['can_edit_roles']).to eq(true)
        end

        it 'does not show the edit roles button when not authorized' do
          expect(admin_account).to receive(:permitted?).with('staff_admins_global_mods').and_return(false)
          get :show, params: { id: 'foo' }
          expect(@controller.view_assigns['can_edit_roles']).to eq(false)
        end

        it 'fetches developer role from RBAC and check if developer role is admin and shadow account when the user is not in an org' do
          expect(Rbac::Membership).to receive(:find_memberships)
          get :show, params: { id: 'foo' }
          expect(@controller.instance_variable_get(:@is_developer_admin_results).blank?).to eq(true)
          expect(@controller.instance_variable_get(:@is_shadow_account_results).blank?).to eq(true)
        end

        it 'fetches developer role from RBAC and check if developer role is admin and shadow account when the user is admin' do
          mock_company = Code::Justin::Tv::Devrel::Rbac::Company.new(:id => "test_company_id")
          mock_membership = Code::Justin::Tv::Devrel::Rbac::CompanyMembership.new(:role => "Administrator", :company => mock_company)
          allow(Rbac::Membership).to receive(:find_memberships)
            .with(anything)
            .and_return([Hash["test_company_id" => mock_membership], nil])
          expect(Rbac::Membership).to receive(:find_memberships)
          get :show, params: { id: 'foo' }
          expect(@controller.instance_variable_get(:@is_developer_admin_results).fetch("test_company_id")).to eq(true)
          expect(@controller.instance_variable_get(:@is_shadow_account_results).fetch("test_company_id")).to eq(false)
        end

        it 'fetches developer role from RBAC and check if developer role is admin and shadow account when the user is shadow account' do
          mock_company = Code::Justin::Tv::Devrel::Rbac::Company.new(:id => "test_company_id")
          mock_membership = Code::Justin::Tv::Devrel::Rbac::CompanyMembership.new(:role => "Shadow_Account", :company => mock_company)
          allow(Rbac::Membership).to receive(:find_memberships)
            .with(anything)
            .and_return([Hash["test_company_id" => mock_membership], nil])
          expect(Rbac::Membership).to receive(:find_memberships)
          get :show, params: { id: 'foo' }
          expect(@controller.instance_variable_get(:@is_developer_admin_results).fetch("test_company_id")).to eq(false)
          expect(@controller.instance_variable_get(:@is_shadow_account_results).fetch("test_company_id")).to eq(true)
        end

        it 'will set find_rbac_memberships_error when rbac returns error' do
          mock_error = Twirp::Error.new(:internal, "some thing is wrong")
          allow(Rbac::Membership).to receive(:find_memberships)
            .with(anything)
            .and_return([{}, mock_error])
          expect(Rbac::Membership).to receive(:find_memberships)
          get :show, params: { id: 'foo' }
          expect(@controller.instance_variable_get(:@find_rbac_memberships_error).msg).to eq(mock_error.msg)
        end
      end

      describe 'report clips' do
        include_context 'an authorized user acting on a twitch user'

        it 'includes aegis link' do
          get :show, params: { id: 'foo' }
          expect(@controller.instance_variable_get(:@aegis_report_clips_url)).to eq("https://aegis.xarth.tv/reportclipsearch/?targetUserID=foo")
        end

        it 'fetches report count' do
          get :show, params: { id: 'foo' }
          expect(@controller.instance_variable_get(:@report_clips)["count"]).to eq(2)
        end
      end
    end
  end

  describe 'edit' do
  end

  describe 'update' do
    let(:permission) { any_permission_in('users', 'twitch_user:view_details', 'twitch_user:update_details') }
    let(:action) { :update }
    let(:redirect_channel) { 'my-redirect-channel' }
    let(:params) { { id: 'foo', twitch_user: { redirect_channel: redirect_channel } } }
    let(:user) { Twitch::User.new }

    before do
      allow(Subscriptions::Token).to receive(:get_token_balance).with(nil).and_return(0)
      expect(Twitch::User).to receive(:find).with('foo').and_return(user)
    end

    context 'an admin with appropriate permissions' do
      let(:update_channel_response) { double('update_channel_response') }

      include_context 'an authorized user acting on a twitch user'

      let(:item) { user }
      let(:unauthorized_attributes) { [] }

      before do
        expect(user).to receive(:channel_results).and_return({})
        expect(user).to receive(:update_channel).and_return(update_channel_response)
        expect(update_channel_response).to receive(:success?).and_return(true)
        expect(History::AddAudit).to receive(:add).and_return(true)

        post action, params: params
      end

      it_behaves_like 'it redirects the user and displays a success message'
      it_behaves_like 'it correctly redacts attributes'
    end

    context 'an admin without appropriate permissions' do
      include_context 'an unauthorized user acting on a twitch user'

      before do
        post action, params: params
      end

      it_behaves_like 'it redirects the user and displays an error'
    end
  end

  describe 'compare_geo_ip' do
    let(:permission) { any_permission_in('users', 'twitch_user:view_personal_information') }
    let(:params) { { id: 'foo', geo_ip: candidate_ip } }
    let(:user) { Twitch::User.new }
    let(:candidate_ip) { '192.168.0.1' }
    let(:historic_ips) { ['127.0.0.1'] }
    let(:fake_geo_lookup) {IpGeo.new('United States', 'US', 'California', 'San Francisco', '37.7749', '122.4194', '1000', 'Webpass, Inc.')}

    context 'an admin with appropriate permissions' do
      include_context 'an authorized user acting on a twitch user'
      include_context 'it creates any tracking call'
      let(:comparison_result) {IpComparisonResult.new(IpComparison::PARTIAL_MATCH, {'Country' => 'United States'})}

      before do
        allow(Subscriptions::Token).to receive(:get_token_balance).with(nil).and_return(0)
        expect(Twitch::User).to receive(:find).with('foo').and_return(user)
        expect(History::QueryAudit).to receive(:ips_for_twitch_user).with(user.id).and_return(historic_ips)
        expect(IpComparison).to receive(:geo_lookup_ip).with(candidate_ip).and_return(fake_geo_lookup)
        expect(IpComparison).to receive(:compare_ips).with(candidate_ip, historic_ips).and_return(comparison_result)
        post :compare_geo_ip, params: params
      end

      it_behaves_like 'it returns a success response'
    end

    context 'an admin without appropriate permissions' do
      include_context 'an unauthorized user acting on a twitch user'

      before do
        expect(Twitch::User).to receive(:find).with('foo').and_return(user)
        post :compare_geo_ip, params: params
      end

      it_behaves_like 'it redirects the user and displays an error'
    end
  end

  describe 'undelete' do
    let(:permission) { any_permission_in('users', 'twitch_user:view_details', 'twitch_user:update_details') }
    let(:action) { :undelete }
    let(:params) { { id: twitch_user_id } }
    let(:user) { twitch_user }

    before(:each) do
      allow(Subscriptions::Token).to receive(:get_token_balance).with(nil).and_return(0)
      expect(Twitch::User).to receive(:find).with(twitch_user_id).and_return(twitch_user)
    end

    context 'an admin with appropriate permissions' do
      include_context 'an authorized user acting on a twitch user'

      context 'a user flagged for rename' do

        before(:each) do

          twitch_user.username_flagged_on = Time.now.utc.round(3).iso8601(3)

          expect(twitch_user)
            .to receive(:undelete)
            .with(ldap_name)
            .and_return(fake_delete_response(true))

          post action, params: params
        end
        it_behaves_like 'it redirects the user and displays a success message'
      end

      context 'a user not flagged for rename' do

        before(:each) do

          expect(twitch_user)
            .to receive(:undelete)
            .with(ldap_name)
            .and_return(fake_delete_response(true))

          post action, params: params
        end
        it_behaves_like 'it redirects the user and displays a success message'
      end
    end

    context 'an admin without appropriate permissions' do
      include_context 'an unauthorized user acting on a twitch user'

      before do
        post action, params: params
      end

      it_behaves_like 'it redirects the user and displays an error'
    end
  end

  describe 'soft_delete' do
    let(:permission) { any_permission_in('users', 'twitch_user:update_details') }
    let(:params) { { id: twitch_user_id } }
    let(:user) { twitch_user }

    before(:each) do
      expect(Twitch::User).to receive(:find).with(twitch_user_id).and_return(twitch_user)
    end

    context 'an admin with appropriate permissions' do
      include_context 'an authorized user acting on a twitch user'

      context 'and flag for rename' do
        before(:each) do

          params[:flagged_for_rename] = true

          allow(Subscriptions::Token).to receive(:get_token_balance).with(nil).and_return(0)
          expect(twitch_user)
            .to receive(:soft_delete)
            .with(ldap_name, true)
            .and_return(fake_delete_response(true))
          post :soft_delete, params: params
        end

        it_behaves_like 'it redirects the user and displays a success message'
      end

      context 'and do not flag for rename' do
        before(:each) do
          allow(Subscriptions::Token).to receive(:get_token_balance).with(nil).and_return(0)
          expect(twitch_user)
            .to receive(:soft_delete)
            .with(ldap_name, false)
            .and_return(fake_delete_response(true))
          post :soft_delete, params: params
        end

        it_behaves_like 'it redirects the user and displays a success message'
      end
    end

    context 'an admin without appropriate permissions' do
      include_context 'an unauthorized user acting on a twitch user'

      before do
        post :soft_delete, params: params
      end

      it_behaves_like 'it redirects the user and displays an error'
    end
  end

  describe 'hard_delete' do
    let(:permission) { any_permission_in('hard_delete', 'users') }
    let(:params) { { id: twitch_user_id } }
    let(:user) { twitch_user }

    before(:each) do
      allow(Subscriptions::Token).to receive(:get_token_balance).with(nil).and_return(0)
      expect(Twitch::User).to receive(:find).with(twitch_user_id).and_return(twitch_user)
    end

    context 'an admin with appropriate permissions' do
      include_context 'an authorized user acting on a twitch user'

      before(:each) do
        post :hard_delete, params: params
      end

      it_behaves_like 'it redirects the user and displays an error'
    end

    context 'an admin without appropriate permissions' do
      include_context 'an unauthorized user acting on a twitch user'

      before do
        post :hard_delete, params: params
      end

      it_behaves_like 'it redirects the user and displays an error'
    end
  end

  describe 'destroy' do
    let(:permission) { any_permission_in('hard_delete', 'users') }
    let(:params) { { id: twitch_user_id } }
    let(:user) { twitch_user }

    before(:each) do
      allow(Subscriptions::Token).to receive(:get_token_balance).with(nil).and_return(0)
      expect(Twitch::User).to receive(:find).with(twitch_user_id).and_return(twitch_user)
    end

    context 'an admin with appropriate permissions' do
      include_context 'an authorized user acting on a twitch user'

      before(:each) do
        expect(twitch_user)
          .to receive(:hard_delete)
          .with(ldap_name)
          .and_return(fake_delete_response(true))
        post :destroy, params: params
      end

      it_behaves_like 'it redirects the user and displays a success message'
    end

    context 'an admin without appropriate permissions' do
      include_context 'an unauthorized user acting on a twitch user'

      before do
        post :destroy, params: params
      end

      it_behaves_like 'it redirects the user and displays an error'
    end
  end
end
