<?php

/**
 * Lifestyle Webconsulting GmbH
 *
 * LICENSE: This Software is the property of Lifestyle Webconsulting GmbH (Aschaffenburg, Germany)
 * and is private by copyright law - it is NOT Freeware.
 *
 * Any unauthorized use of this software without a valid license
 * is a violation of the license agreement and will be prosecuted by
 * civil and criminal law.
 *
 * @copyright  2018 Lifestyle Webconsulting GmbH
 * @link       http://www.life-style.de
 */

namespace Sso\WebserviceBundle\Api\PasswordPolicy\User;

use Sso\WebserviceBundle\Database\Manager as DatabaseManager;
use Sso\WebserviceBundle\Entity\Webservice\Type\User;

/**
 * Class Service
 * @package Sso\WebserviceBundle\Services\PasswordPolicy\User
 */
class Service
{
    /**
     * @var DatabaseManager
     */
    private $dbManager;

    /**
     * Service constructor.
     * @param DatabaseManager $dbManager
     */
    public function __construct(DatabaseManager $dbManager)
    {
        $this->dbManager = $dbManager;
    }

    /**
     * @param string $userGuid
     * @return Model\UserPasswordResetCheckResponse
     * @throws \Exception
     */
    public function checkPasswordRenewRequired($userGuid)
    {
        /* @var User $userEntity */
        $userEntity = $this->dbManager->webservice()->user()->getUserByGuid($userGuid);

        $checkResponse = new Model\UserPasswordResetCheckResponse();

        //set user the policy
        $checkResponse = $this->setUsersPasswordPolicy($userEntity, $checkResponse);

        //next check passwordExpiredFlag
        $checkResponse = $this->validatePasswordExpiredFlag($userEntity, $checkResponse);
        if ($checkResponse->isForcePasswordReset()) {
            return $checkResponse;
        }

        //check if the users password is expired for policy
        return $this->validateForceResetPassword($userEntity, $checkResponse);
    }

    /**
     * @param User $userEntity
     * @param Model\UserPasswordResetCheckResponse $checkResponse
     * @return Model\UserPasswordResetCheckResponse
     */
    private function setUsersPasswordPolicy(User $userEntity, Model\UserPasswordResetCheckResponse $checkResponse)
    {
        $policyEntity = null;

        $policyId = $userEntity->getPasswordPolicy();
        if (0 < strlen($policyId)) {
            $policyEntity = $this->dbManager->webservice()->passwordPolicy()->getOne($policyId);
        }

        // User does not have any policy set - use default policy
        if (null === $policyEntity) {
            $policyEntity = $this->dbManager->webservice()->passwordPolicy()->getDefaultPolicy();
        }

        // No default policy exists
        if (null === $policyEntity) {
            return $checkResponse;
        }

        $usersPasswordPolicy = new Model\UsersPasswordPolicy();
        $usersPasswordPolicy->setPolicyId($policyEntity->getPolicyId());
        $usersPasswordPolicy->setRegEx($policyEntity->getRegEx());
        $usersPasswordPolicy->setChangeDelay($policyEntity->getChangeDelay());
        $usersPasswordPolicy->setValidityDuration($policyEntity->getValidityDuration());

        return $checkResponse->setPasswordPolicy($usersPasswordPolicy);
    }

    /**
     * @param User $userEntity
     * @param Model\UserPasswordResetCheckResponse $checkResponse
     * @return Model\UserPasswordResetCheckResponse
     * @throws \Exception
     */
    private function validatePasswordExpiredFlag(User $userEntity, Model\UserPasswordResetCheckResponse $checkResponse)
    {
        if ($userEntity->isPasswordExpired()) {
            $checkResponse = $this->forceReset($userEntity, $checkResponse);
        }

        return $checkResponse;
    }

    /**
     * @param User $userEntity
     * @param Model\UserPasswordResetCheckResponse $checkResponse
     * @return Model\UserPasswordResetCheckResponse
     * @throws \Exception
     */
    private function validateForceResetPassword(User $userEntity, Model\UserPasswordResetCheckResponse $checkResponse)
    {
        // This may happen, if no default policy exists
        $passwordPolicy = $checkResponse->getPasswordPolicy();
        if (null === $passwordPolicy) {
            return $checkResponse;
        }

        // Looks like a policy without password expire
        $validityDurationDays = $passwordPolicy->getValidityDuration();
        if ($validityDurationDays <= 0) {
            return $checkResponse;
        }

        $lastPasswordChange = $userEntity->getLastPasswordChange();
        if (null === $lastPasswordChange) {
            return $this->forceReset($userEntity, $checkResponse);
        }

        //now some calculations
        $lastPasswordChange = clone $lastPasswordChange;
        $now = new \DateTime();
        $validityDuration = new \DateInterval('P' . $validityDurationDays . 'D');
        $maxAge = $lastPasswordChange->add($validityDuration);

        if ($now >= $maxAge) {
            return $this->forceReset($userEntity, $checkResponse);
        }

        return $checkResponse;
    }

    /**
     * @param User $userEntity
     * @param Model\UserPasswordResetCheckResponse $checkResponse
     * @return Model\UserPasswordResetCheckResponse
     * @throws \Exception
     */
    private function forceReset(User $userEntity, Model\UserPasswordResetCheckResponse $checkResponse)
    {
        $checkResponse->setForcePasswordReset(true);
        $checkResponse->setPasswordExpired(true);

        //now generate a resetToken
        $userEntity->generateResetPasswordToken();
        $this->dbManager->webservice()->user()->saveUser($userEntity);

        $checkResponse->setResetPasswordToken($userEntity->getResetPasswordToken());
        $checkResponse->setResetPasswordTokenExpire($userEntity->getResetPasswordTokenExpire());

        return $checkResponse;
    }
}
