<?php
/***************************************************************
 *  Copyright notice
 *
 *  (c) 2010 Glowbase GmbH
 *
 *  This script is part of the TYPO3 project. The TYPO3 project is
 *  free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  The GNU General Public License can be found at
 *  http://www.gnu.org/copyleft/gpl.html.
 *
 *  This script is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  This copyright notice MUST APPEAR in all copies of the script!
 ***************************************************************/

namespace IMATHUZH\Qfq\Core\Report;

use IMATHUZH\Qfq\Core\Typo3\FrontendUserRepository;
use IMATHUZH\Qfq\Core\Typo3\QfqAuthenticationService;

/**
 * A specialization of {@link Link} class to render authentication columns in reports.
 *
 * Column syntax:
 *    `<username>|<account-data>|<options>|<link-args>...`
 * where
 * + `username`     is a string with the username of the FE account to be authenticated
 * + `account-data` is a &-separated list of key=value pairs of account properties used
 *                  when an account is created or updated
 * + `options`      is a &-separated list of key=value pairs of authentication options:
 *    + `create`
 *    + `update`
 *    + `unlock`
 *    + `activate`
 *    + `enableBy`
 *    + `enableUntil`
 *    + `storage`
 *    + `groups`
 * + `link-args`   is passed to the {@link Link} class to render a link element
 *                 or trigger an HTTP redirection
 */
class AuthLink extends Link {
    /** @var string the username to be authenticated */
    protected string $username;

    /** @var string the string with account data */
    protected string $userdata;

    /** @var string the string with authentication options */
    protected string $features;


    /**
     * Renders the column value into a link element or an HTTP redirection.
     * @param string $str
     * @param string $strDefault
     * @param array &$rcExplicit
     * @return string
     * @throws \CodeException
     * @throws \DbException
     * @throws \IMATHUZH\Qfq\Core\Exception\RedirectResponse
     * @throws \UserFormException
     * @throws \UserReportException
     * @see Link::renderLink
     *
     */
    public function renderLink($str, $strDefault = '', &$rcExplicit = array(),): string {
        // Syntax:
        //    <username>|<user data>|<features>|<rendering>
        // where rendering is either a URL or a string for a column of type _link

        $parts = explode('|', $str, 4);
        $this->username = trim($parts[0]);
        $this->userdata = trim($parts[1] ?? '');
        $this->features = trim($parts[2] ?? '');
        $linkArgs = trim($parts[3] ?? '') ?: $this->getDefaultLinkArgument();
        $content = parent::renderLink($linkArgs, '', $rcExplicit);

        // In case no redirection took place and a link will be rendered,
        // check the account status and populate {{VAR_AUTH_RESULT:V}}
        $this->store->setVar(
            VAR_AUTH_RESULT,
            $this->getAccountStatus($this->username),
            STORE_VAR,
            true
        );

        return $content;
    }

    /**
     * The string to render a link to the current page
     *
     * @return string
     */
    private function getDefaultLinkArgument(): string {
        return 'u:' . $this->getCurrentUrl();
    }

    /**
     * A helper function that returns a URL of the current page
     *
     * @return string
     */
    private function getCurrentUrl(): string {
        return $_SERVER['REQUEST_URI'];
    }

    /**
     * @inheritdoc
     */
    protected function initVars(): array {
        $vars = parent::initVars();
        // Default rendering mode depends on the arguments
        // and it will be computed later
        $vars[NAME_RENDER] = -1;
        $vars[FINAL_HREF] = $this->getCurrentUrl();
        return $vars;
    }

    /**
     * @inheritdoc
     */
    protected function getModeRender(array $vars, array $tokenGiven): array {
        // Update the default rendering mode
        if ($vars[NAME_RENDER] === -1) {
            $vars[NAME_RENDER] = $vars[FINAL_CONTENT]
                ? RENDER_MODE_0_LINK      // text, button, image, etc.
                : RENDER_MODE_9_REDIRECT; // only url
        }
        return parent::getModeRender($vars, $tokenGiven);
    }

    /**
     * Calls Link::doHref() to construct a URL and appends to it query parameters
     * required by the authentication service. These extra parameters are not
     * added to the sip store if a sip parameter is generated by the parent method.
     *
     * @param array &$vars
     * @param array $tokenGiven
     * @param string $devTooltipAppend
     * @return string
     * @throws \CodeException
     * @throws \UserFormException
     * @throws \UserReportException
     */
    protected function doHref(array &$vars, array $tokenGiven, string $devTooltipAppend = '') {
        $href = parent::doHref($vars, $tokenGiven, $devTooltipAppend);
        // create entries in the sip store for user data and authentication features
        $query = QfqAuthenticationService::generateUrlParameters(
            $this->sip,
            $this->username,
            $this->userdata,
            $this->features
        );
        // add parameters to the url
        return $href . (strpos($href, '?') === false ? '?' : '&')
            . http_build_query($query);
    }

    /**
     * Return a constant AUTH_STATUS_NNN that represents the current status
     * of the account with the given username
     *
     * @param string $username
     * @return string
     * @throws \Doctrine\DBAL\DBALException
     * @throws \Doctrine\DBAL\Driver\Exception
     */
    protected function getAccountStatus(string $username): string {
        $status = FrontendUserRepository::getInstance()->getStatus($username);
        return (empty($status) ? AUTH_STATUS_NOT_FOUND : '')
            ?: ($status['locked'] ? AUTH_STATUS_LOCKED : '')
                ?: ($status['expired'] ? AUTH_STATUS_EXPIRED : '')
                    ?: ($status['inactive'] ? AUTH_STATUS_INACTIVE : '')
                        ?: AUTH_STATUS_OK;
    }
}
