require 'spec_helper_min'
require_dependency 'carto/uuidhelper'
require 'factories/carto_visualizations'
require 'support/helpers'
require 'helpers/metrics_helper'

describe Carto::Api::VisualizationExportsController, type: :controller do
  include Carto::UUIDHelper
  include Carto::Factories::Visualizations
  include HelperMethods
  include MetricsHelper

  def create_visualization_export_url(user = nil)
    visualization_exports_url(user_domain: user ? user.username : nil, api_key: user ? user.api_key : nil)
  end

  before(:each) do
    bypass_metrics
  end

  describe 'normal users' do
    before(:all) do
      @user = create(:carto_user, private_maps_enabled: true)
      @user2 = create(:carto_user, private_maps_enabled: true)
    end

    after(:all) do
      # This avoids connection leaking.
      ::User[@user.id].destroy
      ::User[@user2.id].destroy
    end

    describe '#create' do

      before(:each) do
        bypass_named_maps
        @map, @table, @table_visualization, @visualization = create_full_visualization(@user)
        carto_layer = @visualization.layers.find { |l| l.kind == 'carto' }
        carto_layer.options[:user_name] = @user.username
        carto_layer.save
      end

      after(:each) do
        destroy_full_visualization(@map, @table, @table_visualization, @visualization)
      end

      it 'returns 404 for nonexisting visualizations' do
        post_json create_visualization_export_url(@user), visualization_id: random_uuid do |response|
          response.status.should eq 404
        end
      end

      it 'returns 403 for non accessible visualizations' do
        @visualization.privacy = Carto::Visualization::PRIVACY_PRIVATE
        @visualization.save

        post_json create_visualization_export_url(@user2), visualization_id: @visualization.id do |response|
          response.status.should eq 403
        end
      end

      it 'returns 403 for non accessible visualizations (private and protected) for anonymous users' do
        @visualization.privacy = Carto::Visualization::PRIVACY_PRIVATE
        @visualization.save

        post_json create_visualization_export_url, visualization_id: @visualization.id do |response|
          response.status.should eq 403
        end

        @visualization.privacy = Carto::Visualization::PRIVACY_PROTECTED
        @visualization.save

        post_json create_visualization_export_url, visualization_id: @visualization.id do |response|
          response.status.should eq 403
        end
      end

      it 'returns 422 if user_tables_ids param contains user table ids not related to the visualization' do
        post_json create_visualization_export_url(@user),
                  visualization_id: @visualization.id, user_tables_ids: random_uuid do |response|
          response.status.should eq 422
        end
      end

      it 'returns 422 if visualization type is `table`' do
        @visualization.update_attributes(type: Carto::Visualization::TYPE_CANONICAL)
        post_json create_visualization_export_url(@user), visualization_id: @visualization.id do |response|
          response.status.should eq 422
          response.body[:errors].should match(/Only derived visualizations can be exported/)
        end
      end

      it 'enqueues a job and returns the id' do
        job_params = has_entries(
          download_path: regexp_matches(/download$/),
          job_id: regexp_matches(Carto::UUIDHelper::UUID_REGEXP)
        )
        Resque.expects(:enqueue).with(Resque::ExporterJobs, job_params).once
        post_json create_visualization_export_url(@user), visualization_id: @visualization.id do |response|
          response.status.should eq 201
          visualization_export_id = response.body[:id]
          visualization_export_id.should_not be_nil
          visualization_export = Carto::VisualizationExport.find(visualization_export_id)
          visualization_export.visualization_id.should eq @visualization.id
          visualization_export.user_id.should eq @user.id
          visualization_export.state.should eq Carto::VisualizationExport::STATE_PENDING
        end
      end

      it 'enqueues a job and returns the id for anonymous, valid exports' do
        @visualization.privacy = Carto::Visualization::PRIVACY_LINK
        @visualization.save

        Resque.expects(:enqueue).with(Resque::ExporterJobs, anything).once
        post_json create_visualization_export_url, visualization_id: @visualization.id do |response|
          response.status.should eq 201
          visualization_export_id = response.body[:id]
          visualization_export_id.should_not be_nil
          visualization_export = Carto::VisualizationExport.find(visualization_export_id)
          visualization_export.visualization_id.should eq @visualization.id
          visualization_export.user_id.should be_nil
          visualization_export.state.should eq Carto::VisualizationExport::STATE_PENDING
        end
      end
    end

    describe '#show' do
      before(:all) do
        bypass_named_maps
        @visualization = create(:carto_visualization, user: @user)
        @export = create(:visualization_export, visualization: @visualization, user: @user)
        @anonymous_export = create(:visualization_export, visualization: @visualization, user: nil)
      end

      after(:all) do
        @export.destroy
        @visualization.destroy
      end

      def export_url(visualization_export_id, user = nil)
        visualization_export_url(
          user_domain: user ? user.username : nil,
          api_key: user ? user.api_key : nil,
          id: visualization_export_id
        )
      end

      it 'returns 404 for nonexisting exports' do
        get_json export_url(random_uuid, @user) do |response|
          response.status.should eq 404
        end
      end

      it 'returns 403 for exports from other user' do
        get_json export_url(@export.id, @user2) do |response|
          response.status.should eq 403
        end
      end

      it 'returns the visualization export' do
        get_json export_url(@export.id, @user) do |response|
          response.status.should eq 200
          visualization_export = response.body
          visualization_export[:id].should eq @export.id
          visualization_export[:visualization_id].should eq @export.visualization_id
          visualization_export[:user_id].should eq @user.id
          visualization_export[:state].should eq @export.state
        end
      end

      it 'returns the visualization export for anonymous exports' do
        get_json export_url(@anonymous_export.id) do |response|
          response.status.should eq 200
          visualization_export = response.body
          visualization_export[:id].should eq @anonymous_export.id
          visualization_export[:user_id].should be_nil
        end
      end
    end

    describe '#download' do
      before(:all) do
        bypass_named_maps
        @visualization = create(:carto_visualization, user: @user)
        @export = create(:visualization_export, visualization: @visualization, user: @user)
        @anonymous_export = create(:visualization_export, visualization: @visualization, user: nil)
      end

      after(:all) do
        @export.destroy
        @visualization.destroy
      end

      def download_url(visualization_export_id, user = nil)
        visualization_export_download_url(
          user_domain: user ? user.username : nil,
          api_key: user ? user.api_key : nil,
          visualization_export_id: visualization_export_id
        )
      end

      it 'returns 404 for nonexisting exports' do
        get_json download_url(random_uuid, @user) do |response|
          response.status.should eq 404
        end
      end

      it 'returns 403 for exports from other user' do
        get_json download_url(@export.id, @user2) do |response|
          response.status.should eq 403
        end
      end

      it 'downloads the visualization export' do
        Carto::Api::VisualizationExportsController.any_instance.expects(:send_file).
          with(@export.file, type: 'application/zip')
        Carto::Api::VisualizationExportsController.any_instance.stubs(:render)
        get URI::encode("/u/#{@user.username}/#{@export.url}?api_key=#{@user.api_key}"), nil, nil
        response.status.should eq 200
      end
    end
  end

  describe 'shared visualizations' do
    include_context 'visualization creation helpers'
    include_context 'organization with users helper'

    it 'allows exporting a private map shared with a user' do
      visualization = create(:carto_private_visualization, user: @carto_org_user_1)
      share_visualization(CartoDB::Visualization::Member.new(id: visualization.id).fetch, @org_user_2)

      Resque.expects(:enqueue).with(Resque::ExporterJobs, anything).once
      post_json create_visualization_export_url(@carto_org_user_2), visualization_id: visualization.id do |res|
        res.status.should eq 201
        visualization_export_id = res.body[:id]
        visualization_export_id.should_not be_nil
        visualization_export = Carto::VisualizationExport.find(visualization_export_id)
        visualization_export.visualization_id.should eq visualization.id
        visualization_export.user_id.should eq @carto_org_user_2.id
        visualization_export.state.should eq Carto::VisualizationExport::STATE_PENDING
      end
    end
  end
end
