<?php

/*
 * This file is part of the MODX Revolution package.
 *
 * Copyright (c) MODX, LLC
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace MODX\Revolution\Processors\Security\User;

use MODX\Revolution\modUser;
use MODX\Revolution\modUserProfile;
use MODX\Revolution\modX;
use MODX\Revolution\Processors\ModelProcessor;

/**
 * Handles common validation for user processors
 *
 * @package MODX\Revolution\Processors\Security\User
 */
class Validation
{
    /** @var modX $modx */
    public $modx;
    /** @var Create|Update $processor */
    public $processor;
    /** @var modUser $user */
    public $user;
    /** @var modUserProfile $profile */
    public $profile;

    public function __construct(ModelProcessor &$processor, modUser &$user, modUserProfile &$profile)
    {
        $this->processor =& $processor;
        $this->modx =& $processor->modx;
        $this->user =& $user;
        $this->profile =& $profile;
    }

    public function validate()
    {
        $this->checkUsername();
        $this->checkPassword();
        $this->checkEmail();
        $this->checkPhone();
        $this->checkCellPhone();
        $this->checkBirthDate();
        $this->checkBlocked();

        return !$this->processor->hasErrors();
    }

    public function checkUsername()
    {
        $username = $this->processor->getProperty('username');
        if (empty($username)) {
            $this->processor->addFieldError('username', $this->modx->lexicon('user_err_not_specified_username'));
        } elseif (!preg_match('/^[^\'\\x3c\\x3e\\(\\);\\x22]+$/', $username)) {
            $this->processor->addFieldError('username', $this->modx->lexicon('user_err_username_invalid'));
        } elseif (!empty($username)) {
            if ($this->alreadyExists($username)) {
                $this->processor->addFieldError('username', $this->modx->lexicon('user_err_already_exists'));
            }
            $this->user->set('username', $username);
        }
    }

    public function alreadyExists($name)
    {
        return $this->modx->getCount(
            modUser::class,
            [
                'username' => $name,
                'id:!=' => $this->user->get('id'),
            ]
        ) > 0;
    }

    public function checkPassword()
    {
        $newPassword = $this->processor->getProperty('newpassword', null);
        $id = $this->processor->getProperty('id');

        $passwordGenerationMethod = $this->processor->getProperty('passwordgenmethod', 'g');
        if ($passwordGenerationMethod !== 'user_email_specify' && ($newPassword !== null && $newPassword != 'false' || empty($id))) {
            $passwordNotifyMethod = $this->processor->getProperty('passwordnotifymethod', null);
            if (empty($passwordNotifyMethod)) {
                $this->processor->addFieldError('password_notify_method', $this->modx->lexicon('user_err_not_specified_notification_method'));
            }
            if ($passwordGenerationMethod == 'g') {
                $autoPassword = $this->user->generatePassword();
                $this->user->set('password', $autoPassword);
                $this->processor->newPassword = $autoPassword;
            } else {
                $specifiedPassword = $this->processor->getProperty('specifiedpassword');
                $confirmPassword = $this->processor->getProperty('confirmpassword');
                if (empty($specifiedPassword)) {
                    $this->processor->addFieldError('specifiedpassword', $this->modx->lexicon('user_err_not_specified_password'));
                } elseif ($specifiedPassword != $confirmPassword) {
                    $this->processor->addFieldError('confirmpassword', $this->modx->lexicon('user_err_password_no_match'));
                } elseif (strlen($specifiedPassword) < $this->modx->getOption('password_min_length', null, 8, true)) {
                    $this->processor->addFieldError('specifiedpassword', $this->modx->lexicon('user_err_password_too_short'));
                } elseif (!preg_match('/^[^\'\x3c\x3e\(\);\x22\x7b\x7d\x2f\x5c]+$/', $specifiedPassword)) {
                    $this->processor->addFieldError('specifiedpassword', $this->modx->lexicon('user_err_password_invalid'));
                } else {
                    $this->user->set('password', $specifiedPassword);
                    $this->processor->newPassword = $specifiedPassword;
                }
            }
        }
        return $this->processor->newPassword;
    }

    public function checkEmail()
    {
        $email = $this->processor->getProperty('email');
        if (empty($email)) {
            $this->processor->addFieldError('email', $this->modx->lexicon('user_err_not_specified_email'));
        }

        if (!$this->modx->getOption('allow_multiple_emails', null, true)) {
            /** @var modUserProfile $emailExists */
            $emailExists = $this->modx->getObject(modUserProfile::class, ['email' => $email]);
            if ($emailExists) {
                if ($emailExists->get('internalKey') != $this->processor->getProperty('id')) {
                    $this->processor->addFieldError('email', $this->modx->lexicon('user_err_already_exists_email'));
                }
            }
        }
        return $email;
    }

    public function checkPhone()
    {
        $phone = $this->processor->getProperty('phone');
        if (!empty($phone)) {
            if ($this->modx->getOption('clean_phone_number', null, false)) {
                $phone = str_replace(' ', '', $phone);
                $phone = str_replace('-', '', $phone);
                $phone = str_replace('(', '', $phone);
                $phone = str_replace(')', '', $phone);
                $phone = str_replace('+', '', $phone);
                $this->processor->setProperty('phone', $phone);
                $this->profile->set('phone', $phone);
            }
        }
    }

    public function checkCellPhone()
    {
        $phone = $this->processor->getProperty('mobilephone');
        if (!empty($phone)) {
            if ($this->modx->getOption('clean_phone_number', null, false)) {
                $phone = str_replace(' ', '', $phone);
                $phone = str_replace('-', '', $phone);
                $phone = str_replace('(', '', $phone);
                $phone = str_replace(')', '', $phone);
                $phone = str_replace('+', '', $phone);
                $this->processor->setProperty('mobilephone', $phone);
                $this->profile->set('mobilephone', $phone);
            }
        }
    }

    public function checkBirthDate()
    {
        $birthDate = $this->processor->getProperty('dob');
        if (!empty($birthDate)) {
            $date = \DateTimeImmutable::createFromFormat($this->modx->getOption('manager_date_format', null, 'Y-m-d', true), $birthDate);
            if ($date === false) {
                $this->processor->addFieldError('dob', $this->modx->lexicon('user_err_not_specified_dob'));
            }
            $birthDate = $date->getTimestamp();
            $this->processor->setProperty('dob', $birthDate);
            $this->profile->set('dob', $birthDate);
        }
    }

    public function checkBlocked()
    {
        /* blocked until */
        $blockedUntil = $this->processor->getProperty('blockeduntil');
        if (!empty($blockedUntil)) {
            $blockedUntil = str_replace('-', '/', $blockedUntil);
            if (!$blockedUntil = strtotime($blockedUntil)) {
                $this->processor->addFieldError('blockeduntil', $this->modx->lexicon('user_err_not_specified_blockeduntil'));
            }
            $this->processor->setProperty('blockeduntil', $blockedUntil);
            $this->profile->set('blockeduntil', $blockedUntil);
        }

        /* blocked after */
        $blockedAfter = $this->processor->getProperty('blockedafter');
        if (!empty($blockedAfter)) {
            $blockedAfter = str_replace('-', '/', $blockedAfter);
            if (!$blockedAfter = strtotime($blockedAfter)) {
                $this->processor->addFieldError('blockedafter', $this->modx->lexicon('user_err_not_specified_blockedafter'));
            }
            $this->processor->setProperty('blockedafter', $blockedAfter);
            $this->profile->set('blockedafter', $blockedAfter);
        }
    }
}
