require_relative '../../../../spec_helper'
require_relative '../../../../factories/organizations_contexts.rb'
require 'helpers/feature_flag_helper'

describe Carto::Builder::Public::EmbedsController do
  include Warden::Test::Helpers, Carto::Factories::Visualizations, HelperMethods
  include FeatureFlagHelper

  before(:all) do
    bypass_named_maps
    @user = create(:valid_user, private_maps_enabled: true)
    @carto_user = Carto::User.find(@user.id)
    @map = create(:map, user_id: @user.id)
    @visualization = create(:carto_visualization, user: @carto_user, map_id: @map.id, version: 3)
    # Only mapcapped visualizations are presented by default
    Carto::Mapcap.create!(visualization_id: @visualization.id)
  end

  before(:each) do
    bypass_named_maps
    Carto::Visualization.any_instance.stubs(:organization?).returns(false)
    Carto::Visualization.any_instance.stubs(:get_auth_tokens).returns(['trusty_token'])
  end

  after(:all) do
    @map.destroy
    @visualization.destroy
    User[@user.id].destroy
    @feature_flag.destroy
  end

  def stub_passwords(password)
    Carto::Visualization.any_instance.stubs(:has_password?).returns(true)
    Carto::Visualization.any_instance.stubs(:password_valid?).returns(false)
    Carto::Visualization.any_instance.stubs(:password_valid?).with(password).returns(true)
  end

  TEST_PASSWORD = 'manolo'.freeze

  describe '#show' do
    it 'does not display public visualizations without mapcaps' do
      unpublished_visualization = create(:carto_visualization, user: @carto_user, map_id: @map.id, version: 3, privacy: Carto::Visualization::PRIVACY_PUBLIC)
      get builder_visualization_public_embed_url(visualization_id: unpublished_visualization.id)
      response.status.should == 404

      unpublished_visualization.map = nil
      unpublished_visualization.destroy
    end

    it 'does not display link visualizations without mapcaps' do
      unpublished_visualization = create(:carto_visualization, user: @carto_user, map_id: @map.id, version: 3, privacy: Carto::Visualization::PRIVACY_LINK)
      get builder_visualization_public_embed_url(visualization_id: unpublished_visualization.id)
      response.status.should == 404

      unpublished_visualization.map = nil
      unpublished_visualization.destroy
    end

    it 'does not display visualizations without mapcaps' do
      unpublished_visualization = create(:carto_visualization, user: @carto_user, map_id: @map.id, version: 3)
      get builder_visualization_public_embed_url(visualization_id: unpublished_visualization.id)
      response.status.should == 404

      unpublished_visualization.map = nil
      unpublished_visualization.destroy
    end

    it 'displays published layers, not ("live") visualization layers' do
      map, table, table_visualization, visualization = create_full_builder_vis(@carto_user)
      Carto::Mapcap.create!(visualization_id: visualization.id)

      layer = visualization.layers[1]
      old_tile_style = layer.options['tile_style']

      new_layer_style = '#layer { marker-width: 7; }'
      layer.options['tile_style'] = new_layer_style
      layer.save

      get builder_visualization_public_embed_url(visualization_id: visualization.id)

      response.status.should == 200
      response.body.should_not include(new_layer_style)
      response.body.should include(old_tile_style)

      destroy_full_visualization(map, table, table_visualization, visualization)
    end

    it 'embeds visualizations' do
      get builder_visualization_public_embed_url(visualization_id: @visualization.id)

      response.status.should == 200
      response.body.include?(@visualization.name).should be true
    end

    it 'sends caching headers' do
      get builder_visualization_public_embed_url(visualization_id: @visualization.id)

      response.status.should == 200
      response.headers['X-Cache-Channel'].should eq "#{@visualization.varnish_key}:vizjson"
      response.headers['Surrogate-Key'].should eq "#{CartoDB::SURROGATE_NAMESPACE_PUBLIC_PAGES} #{@visualization.surrogate_key}"
      response.headers['Cache-Control'].should eq "no-cache,max-age=86400,must-revalidate,public"
    end

    describe 'connectivity issues' do
      it 'does not need connection to the user db' do
        map, table, table_visualization, visualization = create_full_builder_vis(@carto_user)
        Carto::Mapcap.create!(visualization_id: visualization.id)

        actual_database_name = visualization.user.database_name
        visualization.user.update_attribute(:database_name, 'wadus')

        Rails.logger.expects(:warning).never
        get builder_visualization_public_embed_url(visualization_id: visualization.id)
        response.status.should == 200

        visualization.user.update_attribute(:database_name, actual_database_name)
        destroy_full_visualization(map, table, table_visualization, visualization)
      end
    end

    it 'redirects to builder for v2 visualizations' do
      Carto::Visualization.any_instance.stubs(:version).returns(2)
      get builder_visualization_public_embed_url(visualization_id: @visualization.id)

      response.status.should == 302
    end

    it 'does not include auth tokens for public/link visualizations' do
      get builder_visualization_public_embed_url(visualization_id: @visualization.id)

      response.status.should == 200
      response.body.should include("var authTokens = JSON.parse('[]');")
    end

    it 'does not include google maps if not configured' do
      @map.provider = 'googlemaps'
      @map.save
      @visualization.create_mapcap!
      @user.google_maps_key = ''
      @user.save
      get builder_visualization_public_embed_url(visualization_id: @visualization.id)

      response.status.should == 200
      response.body.should_not include("maps.googleapis.com/maps/api/js")
    end

    it 'includes the google maps client id if configured' do
      @map.provider = 'googlemaps'
      @map.save
      @visualization.create_mapcap!
      @user.google_maps_key = 'client=wadus_cid'
      @user.save
      get builder_visualization_public_embed_url(visualization_id: @visualization.id)

      response.status.should == 200
      response.body.should include("maps.googleapis.com/maps/api/js?v=3.32&client=wadus_cid")
    end

    it 'does not includes google maps if the maps does not need it' do
      @map.provider = 'leaflet'
      @map.save
      @visualization.create_mapcap!
      @user.google_maps_key = 'client=wadus_cid'
      @user.save
      get builder_visualization_public_embed_url(visualization_id: @visualization.id)

      response.status.should == 200
      response.body.should_not include("maps.googleapis.com/maps/api/js")
    end

    it 'includes 3rd party scripts for analytics' do
      Cartodb.with_config(
        trackjs: {
          'customer' => 'fake_trackjs_customer',
          'frequency' => 1
        },
        metrics: {
          'hubspot': {
            'enabled' => true,
            'token' => 'fake_hubspot_token'
          }
        },
        google_tag_manager_id: 'google_tag_manager_id'
      ) do
        get builder_visualization_public_embed_url(visualization_id: @visualization.id)

        response.body.should include("d2zah9y47r7bi2.cloudfront.net/releases/current/tracker.js")
      end
    end

    it 'does not include 3rd party scripts if cookies=0 query param is present' do
      Cartodb.with_config(
        trackjs: {
          'customer' => 'fake_trackjs_customer',
          'frequency' => 1
        },
        metrics: {
          'hubspot': {
            'enabled' => true,
            'token' => 'fake_hubspot_token'
          }
        },
        google_tag_manager_id: 'google_tag_manager_id'
      ) do
        get builder_visualization_public_embed_url(visualization_id: @visualization.id, cookies: '0')

        response.body.should_not include("d2zah9y47r7bi2.cloudfront.net/releases/current/tracker.js")
      end
    end

    it 'does not embed password protected viz' do
      stub_passwords(TEST_PASSWORD)
      @visualization.privacy = Carto::Visualization::PRIVACY_PROTECTED
      @visualization.save

      get builder_visualization_public_embed_url(visualization_id: @visualization.id)

      response.body.include?('Protected map').should be true
      response.status.should == 403
    end

    it 'returns 404 for inexistent visualizations' do
      get builder_visualization_public_embed_url(visualization_id: Carto::UUIDHelper.random_uuid)

      response.status.should == 404
    end

    describe 'private visualizations' do
      it 'cannot be embedded' do
        @visualization.privacy = Carto::Visualization::PRIVACY_PRIVATE
        @visualization.save

        get builder_visualization_public_embed_url(visualization_id: @visualization.id)

        response.body.include?('Embed error | CARTO').should be true
        response.status.should == 404
      end

      it 'can be embedded if logged in' do
        @visualization.privacy = Carto::Visualization::PRIVACY_PRIVATE
        @visualization.save

        login_as(@user)
        get builder_visualization_public_embed_url(visualization_id: @visualization.id)

        response.status.should == 200
        response.body.should include @visualization.name
      end

      it 'include auth tokens in embed' do
        @visualization.privacy = Carto::Visualization::PRIVACY_PRIVATE
        @visualization.save

        login_as(@user)
        get builder_visualization_public_embed_url(visualization_id: @visualization.id)

        response.status.should == 200
        response.body.should include @visualization.name

        @user.reload
        auth_tokens = @user.get_auth_tokens
        auth_tokens.each { |token| response.body.should include token }
      end
    end

    describe 'in organizations' do
      include_context 'organization with users helper'

      before(:each) do
        @org_map = create(:map, user_id: @org_user_owner.id)
        @org_visualization = create(:carto_visualization, user: @carto_org_user_owner, map_id: @org_map.id, version: 3)
        @org_visualization.privacy = Carto::Visualization::PRIVACY_PRIVATE
        @org_visualization.save

        # Only mapcapped visualizations are presented by default
        Carto::Mapcap.create!(visualization_id: @org_visualization.id)

        share_visualization(@org_visualization, @org_user_1)
        Carto::Visualization.any_instance.unstub(:organization?)
        Carto::Visualization.any_instance.stubs(:needed_auth_tokens).returns([])

        @org_map2 = create(:map, user_id: @org_user_owner.id)

        @org_protected_visualization = create(
          :carto_visualization,
          user: @carto_org_user_owner,
          map_id: @org_map2.id,
          version: 3,
          privacy: Carto::Visualization::PRIVACY_PROTECTED,
          encrypted_password: 'xxx',
          password_salt: 'yyy'
        )
        share_visualization(@org_protected_visualization, @org_user_1)
      end

      it 'does not embed private visualizations' do
        get builder_visualization_public_embed_url(visualization_id: @org_visualization.id)

        response.status.should == 404
        response.body.should include 'Embed error | CARTO'
      end

      it 'embeds private visualizations if logged in as allowed user' do
        login_as(@org_user_1)
        get builder_visualization_public_embed_url(visualization_id: @org_visualization.id)

        response.status.should == 200
        response.body.should include @org_visualization.name
      end

      it 'embeds protected visualizations if logged in as allowed user with the right password' do
        login_as(@org_user_1)

        stub_passwords(TEST_PASSWORD)

        post builder_visualization_public_embed_protected_url(visualization_id: @org_protected_visualization.id, password: TEST_PASSWORD)

        response.status.should == 200
        response.body.should include @org_protected_visualization.id
      end

      it 'returns 403 for private visualizations if logged in is not an allowed user' do
        login_as(@org_user_2)
        get builder_visualization_public_embed_url(visualization_id: @org_visualization.id)

        response.status.should == 404
        response.body.should include 'Embed error | CARTO'
      end

      it 'includes auth tokens for privately shared visualizations' do
        login_as(@org_user_1)
        get builder_visualization_public_embed_url(visualization_id: @org_visualization.id)

        response.status.should == 200
        @org_user_1.reload
        auth_tokens = @org_user_1.get_auth_tokens
        auth_tokens.count.should eq 2
        auth_tokens.each { |token| response.body.should include token }
      end

      it 'includes the organizations google maps client id if configured' do
        @org_visualization.map.provider = 'googlemaps'
        @org_visualization.map.save
        @org_visualization.create_mapcap!
        @organization.google_maps_key = 'client=wadus_org_cid'
        @organization.save
        login_as(@org_user_1)
        get builder_visualization_public_embed_url(visualization_id: @org_visualization.id)

        response.status.should == 200
        response.body.should include("maps.googleapis.com/maps/api/js?v=3.32&client=wadus_org_cid")
      end
    end
  end

  describe '#show_protected' do
    it 'does not display visualizations without mapcaps' do
      stub_passwords(TEST_PASSWORD)
      unpublished_visualization = create(:carto_visualization, user: @carto_user, map_id: @map.id, version: 3, privacy: Carto::Visualization::PRIVACY_PROTECTED)
      unpublished_visualization.published?.should be_false

      post builder_visualization_public_embed_protected_url(visualization_id: unpublished_visualization.id, password: TEST_PASSWORD)

      response.body.include?('Invalid password').should be false
      response.status.should == 404

      unpublished_visualization.map = nil
      unpublished_visualization.destroy
    end

    it 'does display published visualizations' do
      stub_passwords(TEST_PASSWORD)
      published_visualization = create(:carto_visualization, user: @carto_user, map_id: @map.id, version: 3, privacy: Carto::Visualization::PRIVACY_PROTECTED)
      Carto::Mapcap.create!(visualization_id: published_visualization.id)
      published_visualization.published?.should be_true

      post builder_visualization_public_embed_protected_url(visualization_id: published_visualization.id, password: TEST_PASSWORD)

      response.status.should == 200

      published_visualization.map = nil
      published_visualization.destroy
    end

    it 'rejects incorrect passwords' do
      stub_passwords(TEST_PASSWORD)
      @visualization.privacy = Carto::Visualization::PRIVACY_PROTECTED
      @visualization.save

      post builder_visualization_public_embed_protected_url(visualization_id: @visualization.id, password: "${TEST_PASSWORD}NO!")

      response.body.include?('Invalid password').should be true
      response.status.should == 403
    end

    it 'accepts correct passwords' do
      stub_passwords(TEST_PASSWORD)
      @visualization.privacy = Carto::Visualization::PRIVACY_PROTECTED
      @visualization.save

      post builder_visualization_public_embed_protected_url(visualization_id: @visualization.id, password: TEST_PASSWORD)

      response.body.include?('Invalid password').should_not be true
      response.body.include?(@visualization.name).should be true
      response.status.should == 200
    end

    it 'includes auth tokens' do
      stub_passwords(TEST_PASSWORD)
      @visualization.privacy = Carto::Visualization::PRIVACY_PROTECTED
      @visualization.save

      post builder_visualization_public_embed_protected_url(visualization_id: @visualization.id, password: TEST_PASSWORD)

      response.status.should == 200
      @visualization.get_auth_tokens.each { |token| response.body.should include token }
    end
  end
end
