<?php

/**
 * user class contains username and its roles
 * This class is used in the base class
 *
 * @author hahn
 * 
 * Axel <axel.hahn@unibe.ch>
 * 2024-08-29  Axel php8 only; added variable types; use short array syntax
 */
class user
{

    /**
     * login name of the current user
     * @var string
     */
    private string $_sUsername = '';

    /**
     * list of groups of the current user
     * @var array
     */
    private array $_aUserGroups = [];

    /**
     * list of roles based on the groups
     * @var array
     */
    private array $_aUserPermmissions = [];

    /**
     * list of projects the current user is involved in
     * @var array
     */
    private $_aProjects = [];

    /**
     * name of the last checked role
     * @var string
     */
    private $_sLastCheckedPermission = false;

    /**
     * Constructor
     * init user with optional given user
     * 
     * @param string $sUser  username to set
     */
    public function __construct(string $sUser = '')
    {
        $this->setUser($sUser);
    }


    // ----------------------------------------------------------------------
    // private functions
    // ----------------------------------------------------------------------


    /**
     * Get string with detected user from current session / basic auth / cli access
     * 
     * @return string
     */
    private function _autoDetectUser(): string
    {
        $sUser = '';
        if (isset($_SESSION) && isset($_SESSION["PHP_AUTH_USER"])) {
            $sUser = $_SESSION["PHP_AUTH_USER"];
        }
        if (!$sUser && isset($_SERVER["PHP_AUTH_USER"])) {
            $sUser = $_SERVER["PHP_AUTH_USER"];
        }
        if (php_sapi_name() == "cli") {
            $sUser = "cliadmin";
        }
        return $sUser;
    }

    /**
     * UNUSED SO FAR
     * Idea: limit user access to a set of projects
     */
    private function _getUser2Projects()
    {
        $sFile = __DIR__ . '/../../../config/inc_user2projects.php';
        return file_exists($sFile)
            ? require $sFile
            : []
        ;
    }

    /**
     * Load roles per user from config
     * @return array
     */
    private function _getUser2Roles(): array
    {
        $sFile = __DIR__ . '/../../../config/inc_user2roles.php';
        return file_exists($sFile)
            ? require $sFile
            : ['admin' => ['admin']]
        ;
    }

    /**
     * TODO: reimplement
     * get the user groups of the current user from an internal source.
     * The function returns a flat aray with names of the groups
     * @return array
     */
    private function _getUserGroups(): array
    {
        $aGroups = [];
        if ($this->_sUsername) {
            $aGroups[] = "authenticated";
            // $aGroups[]='#'.$this->_sUsername;
            $aUserDefinitions = $this->_getUser2Roles();
            foreach (array_keys($aUserDefinitions) as $sGroup) {
                if (array_search($this->_sUsername, $aUserDefinitions[$sGroup]) !== false) {
                    $aGroups[] = $sGroup;
                }
            }
        }
        $this->_aUserGroups = $aGroups;
        return $this->_aUserGroups;
    }

    /**
     * TODO: reimplement
     * get the user roles of the current user from an internal source.
     * The function returns a flat aray with names of the roles
     * @return array
     */
    private function _getUserPermission(): array
    {
        $aRoles = [];
        $aRolesDefinitions = require(__DIR__ . '/../../../config/inc_roles.php');

        // anonymous roles:
        $aRoles = array_merge($aRoles, $aRolesDefinitions['all']);

        foreach (array_keys($aRolesDefinitions) as $sGroup) {
            if ($this->hasGroup($sGroup)) {
                $aRoles = array_merge($aRoles, $aRolesDefinitions[$sGroup]);
            }
        }

        $this->_aUserPermmissions = array_unique($aRoles);
        return $this->_aUserPermmissions;
    }


    // ----------------------------------------------------------------------
    // public ACTIONS
    // ----------------------------------------------------------------------


    /**
     * authenticate a user with the configured methods
     * 
     * @global array  $aConfig  global config
     * @global array  $aParams  params (i.e. GET and POST)
     * 
     * @return boolean
     */
    public function authenticate(): bool
    {
        global $aConfig, $aParams;
        if (!isset($aConfig['auth']) || !is_array($aConfig['auth']) || !count($aConfig['auth']) || !isset($aParams['user'])) {
            return false;
        }
        $sUser = $aParams['user'];
        $sPassword = isset($aParams['password']) ? $aParams['password'] : false;

        foreach (array_keys($aConfig['auth']) as $sAuthMethod) {
            $oUserAuth = false;
            switch ($sAuthMethod) {
                case 'ldap':
                    require_once("userauth.ldap.class.php");
                    $oUserAuth = new userauthLdap($aConfig['auth']['ldap']);
                    break;
                // implement other methods here
                // see userauth.ldap.class.php as simple example

                default:
                    echo 'WARNING: authmethod ' . $sAuthMethod . ' in your config is not implemented in ' . basename(__FILE__) . ' and is useless so far.<br>';
            }
            // if authentication fails then continue and try next method
            if ($oUserAuth && $oUserAuth->authenticate($sUser, $sPassword)) {

                // set a session - it must correspondent with _autoDetectUser()
                // $_SESSION["PHP_AUTH_USER"]=$sUser;
                $this->setUser($sUser);
                return true;
            }
            // if authentication fails then continue and try next method
            if (!$oUserAuth) {
                echo "DEBUG: ERROR oUserAuth waasn't initialized for [$sAuthMethod].<br>";
            }
        }
        return false;
    }

    /**
     * logoff user
     * @return boolean
     */
    public function logoff(): bool
    {
        unset($_SESSION["PHP_AUTH_USER"]);
        $this->setUser();
        return true;
    }

    /**
     * set an authenticated user and get its roles
     * @param string  $sUser  optional: set a given username
     * @return void
     */
    public function setUser(string $sUser = ''): void
    {
        if ($sUser) {
            $this->_sUsername = $sUser;
            $_SESSION["PHP_AUTH_USER"] = $sUser;
        } else {
            // check user from basic auth or cli
            $this->_sUsername = $this->_autoDetectUser();
        }
        $this->_getUserGroups();
        $this->_getUserPermission();
    }

    /**
     * Get html code to display a denied message
     * @return string
     */
    public function showDenied(): string
    {
        return '<div class="alert alert-danger" role="alert">'
            . ($this->_sUsername
                ? t("class-user-error-deny-no-role") . '<br>' . $this->_sUsername . ' --> (' . $this->_sLastCheckedPermission . ')<br>'
                : t("class-user-error-login-required")
            )
            . '</div><br>'
            . '<a href="/deployment/all/login/" class="btn btn-primary">' . t('menu-login') . '</a>'
        ;
    }

    // ----------------------------------------------------------------------
    // public GETTER
    // ----------------------------------------------------------------------


    /**
     * UNUSED SO FAR
     * Idea: limit user access to a set of projects
     */
    public function getUser2Projects()
    {
        return $this->_getUser2Projects();
    }

    /**
     * Get a list of all roles for the current user
     * @return array
     */
    public function getUser2Roles(): array
    {
        return $this->_getUser2Roles();
    }

    /**
     * Get the current username
     * @return string
     */
    public function getUsername(): string
    {
        return $this->_sUsername;
    }

    /**
     * Get a flat array with roles of the current user
     * @return array
     */
    public function getUserGroups(): array
    {
        return $this->_aUserGroups;
    }
    /**
     * Get a flat array with roles of the current user
     * @return array
     */
    public function getUserPermission(): array
    {
        return $this->_aUserPermmissions;
    }

    /**
     * check if the current user has a given role name
     * @param string  $sGroupname  name of the role to check
     * @return bool
     */
    public function hasGroup($sGroupname)
    {
        return !!(array_search($sGroupname, $this->_aUserGroups) !== false);
    }
    /**
     * check if the current user has a given role name
     * @param string  $sPermission  name of the role to check
     * @return boolean
     */
    public function hasPermission($sPermission): bool
    {
        $this->_sLastCheckedPermission = $sPermission;
        $bReturn = !!(array_search($sPermission, $this->_aUserPermmissions) !== false);
        // $this->log(__FUNCTION__ . "($sRolename) -> " . $bReturn ? 'true' : 'false');
        return $bReturn;
    }

}