require 'sequel'
require 'rack/test'
require 'json'
require 'uri'

require 'spec_helper'
require_relative './../../../factories/organizations_contexts'
require 'api/json/synchronizations_controller'


def app
  CartoDB::Application.new
end

describe Carto::Api::PermissionsController do
  include Rack::Test::Methods

  before(:all) do
    @user = create_user
    @api_key = @user.api_key

    @user2 = create_user
  end

  before(:each) do
    delete_user_data @user
    delete_user_data @user2
    @headers = {
      'CONTENT_TYPE' => 'application/json'
    }
    host! "#{@user.username}.localhost.lan"
    @visualization = create(:carto_visualization, user: Carto::User.find(@user.id))
    @entity_id = @visualization.id
  end

  after(:all) do
    bypass_named_maps
    @user.destroy
    @user2.destroy
  end

  describe 'PUT /api/v1/perm' do

    it 'modifies an existing permission' do
      entity_type = Carto::Permission::ENTITY_TYPE_VISUALIZATION

      acl_initial = []
      client_acl_modified = [
        {
          type: Carto::Permission::TYPE_USER,
          entity: { id: @user2.id },
          access: Carto::Permission::ACCESS_READONLY
        }
      ]
      client_acl_modified_expected = [
        {
          type: Carto::Permission::TYPE_USER,
          entity: {
            id: @user2.id,
            username: @user2.username,
            avatar_url: @user2.avatar_url,
            viewer: false,
            base_url: @user2.public_url,
            groups: []
          },
          access: Carto::Permission::ACCESS_READONLY
        }
      ]
      client_acl_final = []

      permission = @visualization.permission
      permission.owner_id.should eq @user.id
      permission.owner_username.should eq @user.username
      permission.acl = acl_initial
      permission.save
      # To force updated_at to change
      sleep(1)

      put "/api/v1/perm/#{permission.id}?api_key=#{@api_key}", { user_domain: @user.username, acl: client_acl_modified}.to_json, @headers
      last_response.status.should == 200
      response = JSON.parse(last_response.body, symbolize_names: true)
      response.fetch(:id).should eq permission.id
      owner_fragment = response.fetch(:owner)
      owner_fragment[:id].should eq permission.owner_id
      owner_fragment[:username].should eq permission.owner_username
      entity_fragment = response.fetch(:entity)
      entity_fragment[:id].should eq @entity_id
      entity_fragment[:type].should eq entity_type
      Time.parse(response.fetch(:created_at)).to_i.should eq permission.created_at.to_i
      Time.parse(response.fetch(:updated_at)).to_i.should_not eq permission.updated_at.to_i
      acl = response.fetch(:acl)
      # base_url deletion because during tests subdomains might not match
      acl[0][:entity].delete(:base_url)
      client_acl_modified_expected[0][:entity].delete(:base_url)
      # The response is a superset of the expectations
      acl.size.should eq 1
      acl_entity = acl[0].delete(:entity)
      client_acl_modified_expected_entity = client_acl_modified_expected[0].delete(:entity)
      acl[0].should include client_acl_modified_expected[0]
      acl_entity.should include client_acl_modified_expected_entity
      put "/api/v1/perm/#{permission.id}?api_key=#{@api_key}", { user_domain: @user.username, acl: client_acl_final}.to_json, @headers
      last_response.status.should == 200
      response = JSON.parse(last_response.body, symbolize_names: true)
      response.fetch(:acl).should eq client_acl_final
    end

  end

  describe 'PUT/DELETE /api/v1/perm' do
    it "makes sure we don't expose unwanted call types" do
      permission = Carto::Permission.create(
        owner_id: @user.id,
        owner_username: @user.username
      )

      expect {
        post "/api/v1/perm/#{permission.id}?api_key=#{@api_key}", nil, @headers
      }.to raise_exception ActionController::RoutingError
      expect {
        delete "/api/v1/perm/#{permission.id}?api_key=#{@api_key}", nil, @headers
      }.to raise_exception ActionController::RoutingError
    end
  end

end

describe 'group permission support' do
  include Rack::Test::Methods
  include_context 'organization with users helper'

  before(:all) do
    @group = create(:carto_group, organization_id: @organization.id)
    @group_2 = create(:random_group, organization_id: @organization.id)

    @headers = {
      'CONTENT_TYPE'  => 'application/json',
      'HTTP_ACCEPT' => 'application/json'
    }
  end

  after(:all) do
    @group.destroy
    @group_2.destroy
  end

  it 'adds group read permission' do
    visualization = create(:carto_visualization, user: Carto::User.find(@org_user_1.id))
    entity_id = visualization.id

    entity_type = Carto::Permission::ENTITY_TYPE_VISUALIZATION

    acl_initial = [ ]
    client_acl_modified = [
      {
        type: Carto::Permission::TYPE_GROUP,
        entity: {
          id:   @group.id,
        },
        access: Carto::Permission::ACCESS_READONLY
      }
    ]
    client_acl_modified_expected = [
      {
        type: Carto::Permission::TYPE_GROUP,
        entity: { id: @group.id, name: @group.name },
        access: Carto::Permission::ACCESS_READONLY
      }
    ]

    permission = visualization.permission
    permission.owner_id.should eq @org_user_1.id
    permission.owner_username.should eq @org_user_1.username
    permission.acl = acl_initial
    permission.save

    put_json(api_v1_permissions_update_url(user_domain: @org_user_1.username, id: permission.id, api_key: @org_user_1.api_key), {acl: client_acl_modified}, @headers) do |response|
      response.status.should == 200
      response_body = response.body.deep_symbolize_keys
      response_body.fetch(:id).should eq permission.id
      owner_fragment = response_body.fetch(:owner)
      owner_fragment[:id].should eq permission.owner_id
      owner_fragment[:username].should eq permission.owner_username
      entity_fragment = response_body.fetch(:entity)
      entity_fragment[:id].should eq entity_id
      entity_fragment[:type].should eq entity_type
      response_body.fetch(:acl).map { |acl| acl.deep_symbolize_keys }.should eq client_acl_modified_expected
    end

    visualization.delete
  end

  it 'creates a shared entity per shared group' do
    visualization = create(:carto_visualization, user: Carto::User.find(@org_user_1.id))
    entity_id = visualization.id

    permission = visualization.permission
    permission.owner_id.should eq @org_user_1.id
    permission.owner_username.should eq @org_user_1.username
    permission.acl.should eq []
    permission.save

    client_acl = [
      {
        type: Carto::Permission::TYPE_GROUP,
        entity: {
          id:   @group.id,
        },
        access: Carto::Permission::ACCESS_READONLY
      }, {
        type: Carto::Permission::TYPE_GROUP,
        entity: {
          id:   @group_2.id,
        },
        access: Carto::Permission::ACCESS_READONLY
      }
    ]
    put_json(api_v1_permissions_update_url(user_domain: @org_user_1.username, id: permission.id, api_key: @org_user_1.api_key), { acl: client_acl }, @headers) do |response|
      response.status.should == 200
      Carto::SharedEntity.where(entity_id: entity_id).count.should == 2
    end

    visualization.delete
  end

end
