<?php

namespace AuthStack\UserDirectory\Service\test\units;

use mageekguy\atoum;
use AuthStack\UserDirectory\Model\Group as GroupModel;
use AuthStack\UserDirectory\Exceptions\AccountException;
use AuthStack\UserDirectory\Model\Account as AccountModel;
use AuthStack\UserDirectory\Model\Organization as OrganizationModel;

class Account extends atoum\test
{
    protected $username = 'dyson.frost@mail.com';
    protected $password = 'itEMDvExB0yYTI/IQH1MSRKbuX1wBj8HmO26AOTBN0o';

    /**
     * Test account flow
     */
    public function testAccountFlow()
    {
        $account = (new AccountModel())
            ->setBaseUrl($this->getBaseUrl())
            ->setBasicAuthorization($this->getAuthToken())
            ->setFirstName('Dyson')
            ->setLastName('Frost')
            ->setEmail($this->username)
            ->setUsername($this->username)
            ->setPassword($this->password)
            ->setAdditional([
                'mail_secondary' => 'dyson.snow@mail.com',
                'birthday' => '19.15.1971'
            ]);

        $this
            // Create the account model
            ->object($account)->isInstanceOf(AccountModel::class)

            ->string($account->getFirstName())->isEqualTo('Dyson')
            ->string($account->getLastName())->isEqualTo('Frost')

            // Create it
            ->object($newacc = $account->create())->isInstanceof(AccountModel::class)

            // Add to system group (it exists for unit testing)
            ->boolean($newacc->addToGroup(1))->isTrue()

            // Add to multiple groups
            ->boolean($newacc->addToMultipleGroups([1]))->isTrue()

            // Remove from group
            ->boolean($newacc->removeFromGroup(1))->isTrue()

            // Get account specific logs (we'll get 5 with default limit)
            ->array($newacc->getLogs())->hasKeys(['data'])

            // Try to authenticate - it fails because account isn't active
            ->exception(function() use (&$newacc)
            {
                $newacc->authenticate($this->username, $this->password);

            })->isInstanceOf(AccountException::class)->hasCode(460)

            // Activate the account
            ->boolean($newacc->activate())->isTrue()

            // Authenticate using wrong username
            ->exception(function() use (&$newacc)
            {
                $newacc->authenticate(str_random(10), str_random(10));

            })->isInstanceOf(AccountException::class)->hasCode(404)

            // Authenticate using wrong password
            ->exception(function() use (&$newacc)
            {
                $newacc->authenticate($this->username, str_random(10));

            })->isInstanceOf(AccountException::class)->hasCode(422)

            // Authenticate using correct credentials
            ->object($acc = $newacc->authenticate($this->username, $this->password))->isInstanceOf(AccountModel::class)

            // Fetch an account from AUD using an ID
            ->object($acc = $acc->read($acc->getID()))->isInstanceOf(AccountModel::class)

            // Update the account
            ->boolean($acc->update(['first_name' => 'Changed', 'last_name' => 'To', 'username' => 'dy@son.com']))->isTrue()

            // Update custom attributes
            ->boolean($acc->updateCustomAttributes(['addr_ln_2' => str_random(32)]))->isTrue()

            // Check that the name has changed
             ->string($acc->getFirstName())->isEqualTo('Changed')

            // Deactivate the account
            ->boolean($acc->deactivate())->isTrue()

            // Try to authenticate, but fail because it's deactivated. Also, use a different username since we changed it
            ->exception(function() use (&$acc)
            {
                $acc->authenticate('dy@son.com', $this->password);

            })->isInstanceOf(AccountException::class)->hasCode(463)

            // Suspend the account
            ->boolean($acc->suspend())->isTrue()

            // Try to authenticate but fail because account is suspended
            ->exception(function() use (&$acc)
            {
                $acc->authenticate('dy@son.com', $this->password);

            })->isInstanceOf(AccountException::class)->hasCode(465)

            // Set as manager
            ->boolean($acc->setManager())->isTrue()
            ->boolean($acc->isManager())->isTrue()

            // Unset manager role
            ->boolean($acc->unsetManager())->isTrue()
            ->boolean($acc->isManager())->isFalse()

            // Set expiry date to be 1 day from now
            ->boolean($acc->setExpiresAt(new \DateTime("now", new \DateTimeZone("UTC"))))->isTrue()

            // Set expiry to be indefinite
            ->boolean($acc->setNeverExpires())->isTrue()

            // Test the search / listing facilities
            ->array($acc->listing(['first_name' => 'Changed']))->hasKeys(['data'])

            // Add account to internal organization
            ->boolean($acc->addToOrganization(1))->isTrue()

            // Add to nonexistent organization
            ->exception(function() use (&$acc)
            {
                $acc->addToOrganization(150000);
            })->isInstanceOf(AccountException::class)->hasCode(404)

            // Remove from organization
            ->boolean($acc->removeFromOrganization())->isTrue()

            // Change username successfully
            ->boolean($acc->changeUsername('new@username.com'))->isTrue()

            // Change username to a taken one, it should produce a conflict due to existing record
            ->exception(function() use (&$acc)
            {
                $acc->changeUsername('admin');
            })->isInstanceOf(AccountException::class)->hasCode(409)

            // Change password policy to default one. We created the account without one
            ->boolean($acc->changePasswordPolicy(1))->isTrue()

            // Try to set to a nonexistent password policy
            ->exception(function() use (&$acc)
            {
                $acc->changePasswordPolicy(17);
            })->isInstanceOf(AccountException::class)->hasCode(404)

            // Change the password successfully
            ->boolean($acc->changePassword(str_random(32)))->isTrue()

            // Change the password to a weak one, password policy kicks in and throws a 422
            ->exception(function() use (&$acc)
            {
                $acc->changePassword('a');
            })->isInstanceOf(AccountException::class)->hasCode(422)

            // Delete the account
            ->boolean($newacc->delete())->isTrue()

            // Read the same account again, but since it's deleted we want 404 back
            ->exception(function() use ($newacc)
            {
                $newacc->read($newacc->getID());
            })->isInstanceOf(AccountException::class)->hasCode(404)
        ;
    }

    public function testCreateOrganizationalAccount()
    {
        $account = (new AccountModel())
            ->setBaseUrl($this->getBaseUrl())
            ->setBasicAuthorization($this->getAuthToken())
            ->setFirstName('Lorem')
            ->setLastName('Ipsum')
            ->setEmail('lorem@ipsum.com')
            ->setUsername('lorem@ipsum.com')
            ->setPassword(str_random(10))
            ->setAdditional([
                'mail_secondary' => 'dyson.snow@mail.com',
                'birthday' => '19.15.1971'
            ]);

        $organization = (new OrganizationModel())
            ->setBaseUrl($this->getBaseUrl())
            ->setBasicAuthorization($this->getAuthToken())
            ->setTitle('LIPSUM ORG')
            ->setDescription('This is test description')
            ->setActive()
            ;

        $this->given($account)
            ->then
            ->array($result = $account->withOrganization($organization))->hasKeys(['organization', 'account'])
            ->object($result['organization'])->isInstanceOf(\AuthStack\UserDirectory\Model\Organization::class)
            ->object($result['account'])->isInstanceOf(AccountModel::class)
        ;
    }
    protected function getBaseUrl($default = 'http://aud.app')
    {
        return function_exists('env') ? env('AUD_URL', $default) : $default;
    }

    protected function getAuthToken($default = 's2fNbDoMKF1hzd0OKrFiphUoLwH29Sgx')
    {
        return base64_encode(function_exists('env') ? env('AUD_TOKEN', $default) : $default);
    }
}