require_relative 'helper'

class MxHeroAPITest < MiniTest::Unit::TestCase

  def setup
    @api = MxHero::API::Client.new(username: TEST_API_USER, password: TEST_API_PASSWORD, verbose: false, api_url: TEST_API_URL)
  end

  def domain
    TEST_API_DOMAIN
  end

  def test_verbose
    api = MxHero::API::Client.new
    assert ! api.verbose?, 'Verbose is false by default'

    api.verbose = true
    assert api.verbose?, 'Verbose state must be changed'
  end

  def test_update_rule
    VCR.use_cassette('update_rule') do
      rules = obtain_rules(domain)
      existing_rule = rules.first
      updated_rule = existing_rule.clone
      
      property = updated_rule[:properties].find { |property| property[:propertyKey] == 'return.message' }
      property[:propertyValue] = 'another content'
      updated_rule[:name] = updated_rule[:name] + ' more name'

      assert @api.update_rule(updated_rule), 'the rule not must be updated'

      rule = @api.domain_rule(updated_rule[:domain], updated_rule[:id])
      assert updated_rule, rule
    end
  end

  def test_domain_rule
    VCR.use_cassette('domain_rule') do
      rules = obtain_rules(domain)
      one_rule = rules.first
      rule = @api.domain_rule(domain, one_rule[:id])
      assert one_rule[:id], rule[:id]
      assert one_rule, rule
      assert domain, rule[:domain]
    end
  end

  def test_rules_for_domain
    VCR.use_cassette('rules_for_domain') do
      response = @api.rules_for_domain(domain)
      assert response.code == 200
      rules = response.msg
      assert rules.is_a? Array
    end
  end

  def test_rules_for_domain_filter_component
    VCR.use_cassette('rules_for_domain_by_component') do
      component = 'org.mxhero.feature.signature'
      response = @api.rules_for_domain(domain, component)
      assert response.code == 200
      rules = response.msg
      assert rules.all? { |rule| rule[:component] == component }, "All the rules must be of component type #{component}"
    end
  end

  def test_domains
    VCR.use_cassette('domains') do
      domains = @api.domains
      assert domains.key?(:elements)
      assert domains.key?(:totalElements)
      assert domains.key?(:totalPages)
      assert domains.key?(:actualPage)
    end
  end

  def test_accounts_by_domain
    VCR.use_cassette('accounts_from_domain') do
      response = @api.accounts_by_domain(domain)
      must_have_the_keys(response, %w( elements totalElements totalPages actualPage ))
      response[:elements].each do |account|
        must_have_the_keys(account, %w( account domain createdDate updatedDate group aliases dataSource ) )
        account[:aliases].each { |t_alias| must_have_the_keys(t_alias, %w( name domain dataSource )) }
      end
    end
  end

  def test_accounts_without_group
    VCR.use_cassette('accounts_without_group') do
      response = @api.accounts_by_domain(domain, without_group: true)
      assert response.key? :elements
      account = response[:elements].first
      start_with = account[:account][0..2]
      
      response = @api.accounts_by_domain(domain, without_group: true, account: start_with)
      assert response[:totalElements] >= 1
      find_account = response[:elements].first
      assert_equal start_with, find_account[:account][0..2]
    end
  end

  def test_accounts_by_domain_filtered
    VCR.use_cassette('accounts_filtered') do
      # The test environment have 4 accounts: 
      # agent.smith, john.doe, test, test1, test2
      response = @api.accounts_by_domain(domain, account: 'test')
      assert_equal 3, response[:elements].count
      response = @api.accounts_by_domain(domain, account: 'john.doe')
      assert_equal 1, response[:elements].count
      response = @api.accounts_by_domain(domain, account: 'test1')
      assert_equal 1, response[:elements].count
    end
  end

  def test_accounts_by_domain_paginated
    VCR.use_cassette('accounts_from_domain_paginated') do
      response = @api.accounts_by_domain(domain, limit: 4, offset: 1)
      must_have_the_keys(response, %w( elements totalElements totalPages actualPage ))
      assert_equal 4, response[:elements].count
      assert_equal 1, response[:actualPage]
    end
  end

  def test_delete_rule
    VCR.use_cassette('delete_rule') do
      rules = obtain_rules(domain)
      one_rule = rules.first      
      response = @api.delete_rule(domain, one_rule[:id])
      assert response
    end
  end
  
  #def test_try_to_create_rule_that_alredy_exist
  #  VCR.use_cassette('create_rule_alredy_exist') do
  #    response = @api.create_rule(create_rule_msg)
  #    #assert_equal 201, response.code
  #    new_response = @api.create_rule(create_rule_msg)
  #    assert_equal 500, new_response.code
  #    assert_equal "rules.already.exists.for.component", new_response.msg[:developerMessage]
  #  end
  #end
  
  def test_create_rule_for_domain
    VCR.use_cassette('create_rule_for_domain') do
      delete_all_rules  
      response = @api.create_rule_for_domain(domain, rule)
      assert response.code == 201
      new_response = @api.create_rule_for_domain(domain, rule)
      assert new_response.code == 500
      assert_equal "rules.already.exists.for.component", new_response.msg[:developerMessage]
    end
  end

  def test_account_properties
    VCR.use_cassette('account_properties') do 
      account = 'test'
      response = @api.account_properties(domain, account)
      assert_equal 200, response.code
      assert response.is_a? MxHero::API::Response
      properties = response.msg
      %w( email fax lastname mobile name opt1 opt2 opt3 telephone ).each do |property| 
        assert properties.key? property
      end
    end
  end

  def test_account_propertes_not_found
    VCR.use_cassette('account_properties_not_found') do 
      begin
        account = 'xxxxx'
        response = @api.account_properties(domain, account)
        assert_equal 404, response.code
        assert response.is_a? MxHero::API::Response
      rescue
        flunk 'error!'
      end
    end
  end

  def test_update_account_properties
    VCR.use_cassette('update_account_properties') do 
      account = 'test'
      response = @api.account_properties(domain, account)
      properties = response.msg
      properties['lastname'] = ( properties['lastname'] || '' ) + ' CHANGED' 
      properties['name']     = ( properties['name'] || '' ) + ' CHANGED' 
      properties['fax']      = nil
      response = @api.update_account_properties(domain, account, properties)
      assert_equal 200, response.code

      response = @api.account_properties(domain, account)
      assert_equal properties['lastname'], response.msg['lastname']
      assert_equal properties['name'], response.msg['name']
      assert_equal properties['fax'], response.msg['fax']
    end
  end
  
  def test_update_accounts_group_scope
    VCR.use_cassette('update_accounts_group_scope') do

      accounts = [ { account: 'john.doe', group: 'development' } ]
      response = @api.update_accounts(domain, accounts, :groups)
      account = @api.accounts_by_domain(domain, account: 'john.doe')[:elements].first
      assert_equal 'development', account[:group]
    end
  end

  def test_update_accounts_properties_scope
    VCR.use_cassette('update_accounts_properties_scope') do
      props = @api.account_properties(domain, 'john.doe').msg
      changed = props.clone
      original = changed['mobile']
      changed['mobile'] = original + '011'

      accounts = [ { account: 'john.doe', properties: changed } ]

      @api.update_accounts(domain, accounts, :properties)

      properties = @api.account_properties(domain, 'john.doe').msg
      assert_equal original + '011', properties['mobile']
    end
  end

  

  def test_domain
    VCR.use_cassette('test_domain') do
      domain = @api.domain('mxhero.com')

      response = @api.domain('xxxx.xxx')
      assert response.nil?
    end

    def @api.call(_, _)
      Class.new do
        def ok?
          false
        end

        def code
          500
        end

        def content
          ""
        end
      end.new
    end

    err = assert_raises ::RuntimeError do 
      @api.domain('nop') 
    end

    assert_match /An error ocurred when try to fetch the domain nop/, err.message
    assert_match /HTTP code: 500/, err.message
  end

  def test_ldap_info
    VCR.use_cassette('ldap_info') do
      ldap = @api.ldap_info('mxhero.com')
      %w{domain directoryType addres port sslFlag user password filter 
      base nextUpdate lastUpdate error overrideFlag 
      dnAuthenticate properties}.each do |field|
        assert ldap.key?(field.to_sym), "must retrieve ldap hash info with key: #{field}"
      end
    end
  end

  def test_move_to_trial
    VCR.use_cassette('move_to_trial') do
      response = @api.move_to_trial('mxhero.com')
      assert response
    end
  end

  def test_system_properties
    VCR.use_cassette('system_properties') do
      response = @api.system_properties
      # puts "Response: #{response}"
      assert response.is_a? Hash
      assert_equal 'en_US', response['default.user.language']
      assert_equal 'admin@example.com', response['mail.admin']
    end
  end

  def test_specific_system_properties
    VCR.use_cassette('system_properties_element') do
      response = @api.system_properties('default.user.language')
      assert_equal 'en_US', response
    end
  end

  def test_save_system_property
    VCR.use_cassette('system_properties_create') do
      response = @api.save_system_property('test.key', 'test.value')
      assert response
      assert_equal 'test.value', @api.system_properties('test.key')
      
      response = @api.save_system_property('test.key', 'test.value.change')
      assert response
      assert_equal 'test.value.change', @api.system_properties('test.key')
    end
  end

  def test_user_domains
    VCR.use_cassette('user_domains') do
      domains = @api.user_domains('mxhero@mxhero.com')
      assert domains.all? { |domain| ['mxhero.com', 'stage.mxhero.com'].include?(domain) }

      domains = @api.user_domains('admin')
      assert domains.empty?

      domains = @api.user_domains('not_existent_account')
      assert domains.nil?
    end
  end

  def test_fetch_user
    VCR.use_cassette('fetch_user') do
      user = @api.fetch_user('mxhero@mxhero.com')
      refute user.nil?
      
      user = @api.fetch_user('not-exist@mxhero.com')
      assert user.nil?
    end
  end

  def test_create_user
    VCR.use_cassette('create_user') do
      user_info = {
        name: 'test', lastName: 'test last name', 
        notifyEmail: 'test@mxhero.com', userName: 'test@mxhero.com'
      }
      created = @api.create_user(user_info, 'mxhero.com', 'stage.mxhero.com')
      assert created
    end
  end


  private
  
  def delete_all_rules
    response = @api.rules_for_domain(domain)
    response.msg.each do |rule| 
      @api.delete_rule(domain, rule[:id]) 
    end
  end

  def obtain_rules(domain)
    response = @api.rules_for_domain(domain)
    if response.msg.nil? || response.msg.empty? 
      created = @api.create_rule_for_domain(domain, rule)
      return [ created.msg ]
    end
    response.msg
  end

  def must_have_the_keys(hash, keys)
    keys.each do |key|
      assert hash.key?(key.to_sym), "must have the key #{key}"
    end
  end

  def rule_msg_with_domain(domain)
    msg = create_rule_msg
    msg[:domain] = domain
    msg
  end

  def random_name
    (0...15).map{ ('a'..'z').to_a[rand(26)] }.join
  end

  def create_rule_msg(name = nil)
    {
      #"domain" => "tesla.com",
      twoWays: false,
      enabled: true,
      name: name || "Email footer",
      created: 1369682598000,
      fromDirection:  { directionType: "domain", domain: domain, freeValue: domain },
      toDirection:    { directionType: "domain", domain: domain, freeValue: domain },
      properties: [{ propertyKey: "return.message", propertyValue: "footer content" }],
      component: "org.mxhero.feature.disclaimer"
    }
  end

  def rule
    { :domain => domain, 
      :twoWays => false, 
      :enabled => true, 
      :name => "The rules for sales", 
      :created => "1375998105095", 
      :fromDirection => { :id => nil, 
                          :directionType => "domain", 
                          :freeValue => domain, 
                          :domain => domain, 
                          :account => nil, :group => nil }, 
      :toDirection => { :id => nil, 
                        :directionType => "anyone", 
                        :freeValue => "Anyone", 
                        :domain => domain, 
                        :account => nil, :group => nil}, 
      :properties => [
        { :propertyKey => "return.message", 
          :propertyValue => "<hr /><p><em><span style=\"color:green\">Think before you print and save a tree.</span></em></p>" }, 
        { :propertyKey => "return.message.plain", 
          :propertyValue => "\r\n----------------\r\nThink before you print and save a tree.r\n\r\n"}, 
        { :propertyKey => "interParsing", 
          :propertyValue => true}
      ], 
      :component => "org.mxhero.feature.disclaimer" }
  end

 end