<?php

namespace CodeJetter\core;

use CodeJetter\core\io\Output;
use CodeJetter\core\security\Security;

if (session_status() == PHP_SESSION_NONE) {
    session_start();
}

/**
 * Class FormHandler.
 */
class FormHandler
{
    private $formName;

    /**
     * @param $formName
     */
    public function __construct($formName)
    {
        $this->setFormName($formName);
    }

    /**
     * @param null $hashAlgorithm
     *
     * @throws \Exception
     *
     * @return string
     */
    public function setAntiCSRF($hashAlgorithm = null)
    {
        /**
         * start getting a new token.
         */
        $security = new Security();

        if ($hashAlgorithm !== null) {
            $security->setHashAlgorithm($hashAlgorithm);
        }
        /*
         * finish getting a new token
         */

        // clean the session to avoid DoS attack
        if (isset($_SESSION['forms'][$this->getFormName()])
            && count($_SESSION['forms'][$this->getFormName()]) >= $this->getMaxFormTokens()) {
            unset($_SESSION['forms'][$this->getFormName()]);
        }

        // set into the session
        $token = $security->generateToken();

        return $_SESSION['forms'][$this->getFormName()][$token] = $token;
    }

    /**
     * @param null   $hashAlgorithm
     *                              Does not use for the global one currently
     * @param null   $name
     * @param null   $id
     * @param string $mode
     *                              Values are global, perForm
     *
     * @throws \Exception
     *
     * @return string
     */
    public function generateAntiCSRFHtml($hashAlgorithm = null, $name = null, $id = null, $mode = 'global')
    {
        // generate id html
        $id = !empty($id) ? "id='{$id}'" : '';

        switch ($mode) {
            case 'global':
                $tokenName = Registry::getConfigClass()->get('defaultGlobalCSRFHtmlTokenName');
                $name = !empty($name) ? $name : $tokenName;

                $app = App::getInstance();
                $tokenValue = $app->getAntiCSRFToken();

                $html = "<input type='hidden' name='{$name}' {$id} value='{$tokenValue}'>";
                break;
            case 'perForm':
                $tokenName = Registry::getConfigClass()->get('defaultCSRFHtmlTokenName');
                $name = !empty($name) ? $name : $tokenName;

                $html = "<input type='hidden' name='{$name}' {$id} value='{$this->setAntiCSRF($hashAlgorithm)}'>";
                break;
            default:
                throw new \Exception('Mode is not valid');
                break;
        }

        return $html;
    }

    /**
     * @param      $token
     * @param bool $resetAfterChecking
     *
     * @throws \Exception
     *
     * @return Output
     */
    public function checkAntiCSRF($token, $resetAfterChecking = false)
    {
        $output = new Output();

        if (!empty($token)
            && isset($_SESSION['forms'][$this->getFormName()])
            && array_key_exists($token, $_SESSION['forms'][$this->getFormName()])) {
            $output->setSuccess(true);

            // reset token after checking if $resetAfterChecking is set to true
            if ($resetAfterChecking === true) {
                // remove this token and generate a new one
                unset($_SESSION['forms'][$this->getFormName()][$token]);
                $output->setData($this->setAntiCSRF());
            }
        } else {
            $output->setSuccess(false);
        }

        return $output;
    }

    /**
     * @return string
     */
    public function getFormName()
    {
        return $this->formName;
    }

    /**
     * @param string $formName
     */
    public function setFormName($formName)
    {
        $this->formName = $formName;
    }

    /**
     * @return int
     */
    public function getMaxFormTokens()
    {
        if (!isset($this->maxFormTokens)) {
            $this->setMaxFormTokens(Registry::getConfigClass()->get('maxFormTokens'));
        }

        return $this->maxFormTokens;
    }

    /**
     * @param int $maxFormTokens
     */
    public function setMaxFormTokens($maxFormTokens)
    {
        $this->maxFormTokens = $maxFormTokens;
    }
}
