<?php

namespace AuthStack\Driver\LDAP\tests\units;

use AuthStack\Driver\LDAP\User;
use mageekguy\atoum;
use function Buckhill\config;
use AuthStack\Driver\Errors\ConnectionError;
use AuthStack\Driver\LDAP\LDAP as LDAPDriver;
use AuthStack\Driver\Errors\NoAuthSourceError;
use AuthStack\Driver\Errors\NoUserObjectError;
use AuthStack\Driver\Errors\InsufficientCredentialsError;

class LDAP extends atoum
{

    public function testConnectionAuthenticationAndProvisioning()
    {
        $this
            ->given(config()->set('LDAPTest', $this->getConfig()))
            ->and($ldap = LDAPDriver::initialize('LDAPTest', 2))
            ->then
                ->object($ldap)->isInstanceOf(LDAPDriver::class)
                ->boolean($ldap->connect())->isTrue()
                ->integer($ldap->getAuthSourceId())->isEqualTo(2)
                ->boolean($ldap->authenticate($this->getValidCredentials()))->isTrue()
                ->object($ldap->getUser())->isInstanceOf(User::class)
                ->string($ldap->lastError())->isEmpty()
                ->boolean($ldap->passwordChangeAllowed())->isFalse()
                ->boolean($ldap->changePassword([]))->isFalse()
        ;
    }


    public function testRetrieveByUsername()
    {
        $this
            ->given(config()->set('LDAPTest', $this->getConfig()))
            ->and($ldap = LDAPDriver::initialize('LDAPTest', 2))
            ->and($ldap->connect())
            ->then
                ->object($ldap)->isInstanceOf(LDAPDriver::class)
                ->object($ldap->getUserByUsername('nikola@buckhill.co.uk'))->isInstanceOf(User::class)
        ;
    }

    public function testRetrieveByPrimaryKey()
    {
        $this
            ->given(config()->set('LDAPTest', $this->getConfig()))
            ->and($ldap = LDAPDriver::initialize('LDAPTest', 2))
            ->and($ldap->connect())
            ->then
            ->object($ldap)->isInstanceOf(LDAPDriver::class)
            ->object($ldap->getUserByPrimaryKey('nikola@buckhill.co.uk'))->isInstanceOf(User::class)
            ->boolean($ldap->getUser()->isAdmin())->isTrue()
        ;
    }

    public function testInvalidConfigurationKillsConnecting()
    {
        $this
            ->given(config()->set('LDAPBadTest', $this->getBadConfig()))
            ->and($ldap = LDAPDriver::initialize('LDAPBadTest', 2))
            ->then
            ->exception(function () use ($ldap) {
                $result = $ldap->connect();
            })
            ->isInstanceOf(ConnectionError::class)
        ;
    }

    public function testNoConnection()
    {
        $this
            ->given(config()->set('LDAPTest', $this->getConfig()))
            ->and($ldap = LDAPDriver::initialize('LDAPTest', 2))
            ->then
                ->exception(function () use ($ldap) {
                    $conn = $ldap->getConnection();
                })
                    ->isInstanceOf(ConnectionError::class)
        ;
    }

    public function testAuthenticationFails()
    {
        $this
            ->given(config()->set('LDAPTest', $this->getConfig()))
            ->and($ldap = LDAPDriver::initialize('LDAPTest', 2))
            ->then
                ->exception(function () use ($ldap) {
                    $ldap->authenticate([]);
                })
                    ->isInstanceOf(InsufficientCredentialsError::class)

                ->exception(function () use ($ldap) {
                    $ldap->authenticate(['username' => 'test']);
                })
                    ->isInstanceOf(InsufficientCredentialsError::class)

                ->exception(function () use ($ldap) {
                    $ldap->authenticate(['password' => 'test']);
                })
                    ->isInstanceOf(InsufficientCredentialsError::class)

                ->exception(function () use ($ldap) {
                    $user = $ldap->getUser();
                })
                    ->isInstanceOf(NoUserObjectError::class)

                ->exception(function () use ($ldap) {

                    $ldap->setAuthSourceId(null);
                    $authsource_id = $ldap->getAuthSourceId();
                })
                    ->isInstanceOf(NoAuthSourceError::class)

        ;
    }

    protected function getConfig()
    {
        return [
            'dc' => 'dc=buckhill,dc=co,dc=uk',
            'ou' => 'ou=users',
            'uid' => 'uid',
            'attributes' => [
                'memberof', 'cn', 'dn', 'mail', 'uid', 'givenname', 'sn',
                'telephonenumber', 'postaladdress', 'o', 'l', 'postalcode', 'st',
            ],
            'administrative' => [
                // Which LDAP attribute to inspect
                'attribute' => 'memberof',

                // What value should the attribute contain
                'value' => 'cn=administrators,ou=groups,dc=buckhill,dc=co,dc=uk'
            ],
            'settings' => [
                // The key is used by User class and it maps to value
                // So, if we want to pull out someone's name, we need to change "givenname" to something else
                // since the 'name' will be used by the library to find out which LDAP attribute to pull out
                'name' => 'givenname',
                'lastname' => 'sn',
                'email' => 'mail'
            ],
            'connection' => [
                'host' => '10.0.0.188',
                'port' => 389,
                'base_dn' => 'dc=buckhill,dc=co,dc=uk',
                'TLS' => false,
                'options' => [
                    [
                        'name' => LDAP_OPT_PROTOCOL_VERSION,
                        'value' => 3,
                    ],
                ],
            ],
        ];
    }

    protected function getBadConfig()
    {
        return [
            'dc' => 'dc=buckhill,dc=co,dc=uk',
            'ou' => 'ou=users',
            'uid' => 'uid',
            'attributes' => [
                'memberof', 'cn', 'dn', 'mail', 'uid', 'givenname', 'sn',
                'telephonenumber', 'postaladdress', 'o', 'l', 'postalcode', 'st',
            ],
            'connection' => [
                'host' => 'this is just a bad string',
                'port' => 1234,
                'base_dn' => 'dc=buckhill,dc=co,dc=uk',
                'TLS' => false,
                'options' => [
                    [
                        'name' => 'bad',
                        'value' => 'options',
                    ],
                ],
            ],
        ];
    }

    protected function getValidCredentials()
    {
        return [
            'username' => 'test@buckhill.co.uk',
            'password' => 'test',
        ];
    }
}
