<?php

declare(strict_types=1);

/**
 * This file is part of LEPTON Core, released under the GNU GPL
 * Please see LICENSE and COPYING files in your package for details, specially for terms and warranties.
 * 
 * NOTICE:LEPTON CMS Package has several different licenses.
 * Please see the individual license in the header of each single file or info.php of modules and templates.
 *
 * @author          LEPTON Project
 * @copyright       2010-2026 LEPTON Project
 * @link            https://lepton-cms.org
 * @license         https://gnu.org/licenses/gpl.html
 * @license_terms   please see LICENSE and COPYING files in your package
 *
 */

class LEPTON_session
{
    use LEPTON_singleton;

    public static $instance;

    // Default constructor of the class
    final public function __construct()
    {
        // return the class instance
        //return self::getInstance();
    }

    /**
     * Static function to set a cookie.
     *
     * @params  string      $name           Name of cookie
     * @params  string|int  $value          New cookie value
     * @params  array       $options        Array with settings, see PHP documentation
     * @params  bool        $mustExists     Boolean true/false: set cookie only if exists
     * @params  bool        $mergeDefault   Boolean true/false: merge input options with defaults
     *
     * @return  bool True if ok, otherwise false if failed.
     *
     * @see https://www.php.net/manual/de/function.setcookie.php
     * @see https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy
     * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
     *
     */
    public static function set_cookie (
        string $name,
        string|int $value,
        array $options = [],
        bool $mustExists = false,
        bool $mergeDefault = true
    ): bool
    {
        // check if name exists in $_COOKIE
        if ((true === $mustExists ) && (false === isset($_COOKIE[ $name ])))
        {
            return false;
        }

        // clear options, remove empty settings
        $temp = $options;
        $options = [];
        foreach( $temp as $optkey => $optvalue )
        {
            if ($optvalue != "")
            {
                $options[$optkey] = $optvalue;
            }
        }

        // merge options array with defaults
        if (true === $mergeDefault )
        {
            $defaults = self::get_cookie_defaults();
            $options = array_merge($defaults, $options);
        }

        //  Update cookie settings, see: https://www.php.net/manual/de/function.setcookie.php
        $cookie = setcookie($name, $value, $options);
        
        return $cookie;
    }

    /**
     * Static function to delete a cookie and SESSION: overwrite existing cookie with old expires value and empty $_SESSION
     *
     * @params string      $name       Name of cookie
     * @params string|int  $value      New cookie value
     * @params array       $options    Array with settings, see PHP documentation
     * @return bool
     *
     */
    public static function deleteCookieSession(string $name, string|int $value = "", array $options = []): bool
    {
        // check if name exists in $_COOKIE
        if (false === isset($_COOKIE[$name]))
        {
            return false;    // not exists means also cookie not exists
        }

        // overwrite session array
        $_SESSION = [];

        // set/overwrite expire option
        $options["expires"] = 1;    // 01.01.1970 00:00:01

        // delete session cookie if set
        $returnVal = self::set_cookie($name, $value, $options);

        // overwrite session array again
        $_SESSION = [];

        // delete the session itself
        session_destroy();

        return $returnVal;
    }

    /**
     *  get cookie default settings
     *
     */
    public static function get_cookie_defaults(): array
    {
        $iniValues = self::getConfigValuesFromIni();
        
        // return the defaults
        return [
            /**
             * Session.cookie_lifetime specifies the lifetime of the cookie in seconds which
             * is sent to the browser. The value 0 means "until the browser is closed."
             * Defaults to 0.
             */
            "expires" => time() + ($iniValues['lifetime'] ?? (3 * 3600)), // three hours

            /**
             * session.cookie_path specifies path to set in the session cookie. Defaults to /.
             */
            "path" => "/",

            /**
             *  Session.cookie_domain specifies the domain to set in the session cookie.
             * Default is none at all meaning the host name of the server which generated
             * the cookie according to cookies specification.
             */
            "domain" => "",

            /**
             * Session.cookie_secure specifies whether cookies should only be sent over secure
             * connections. Defaults to off.
             */
            "secure" => (isset($_SERVER['HTTPS'])
                    ? (strtolower( $_SERVER['HTTPS'] )  == "on") // bool
                    : false
            ),

            /**
             * Session.cookie_httponly Marks the cookie as accessible only through the HTTP protocol.
             * This means that the cookie won't be accessible by scripting languages, such
             * as JavaScript. This setting can effectively help to reduce identity theft
             * through XSS attacks (although it is not supported by all browsers).
             */
            "httponly" => $iniValues['httponly'] ?? true,

            /**
             * Session.cookie_samesite allows servers to assert that a cookie ought not to be sent
             * along with cross-site requests. This assertion allows user agents to mitigate the
             * risk of cross-origin information leakage, and provides some protection against
             * cross-site request forgery attacks. Note that this is not supported by all browsers.
             * An empty value means that no SameSite cookie attribute will be set.
             * Lax and Strict mean that the cookie will not be sent cross-domain for POST requests;
             * Lax will sent the cookie for cross-domain GET requests, while Strict will not.
             */
            "samesite" => $iniValues['samesite'] ?? "Lax"
        ];
    }


    public static function getConfigValuesFromIni(): array
    {
        $vals = [
            'samesite' => ['type' => 'string', 'default' => 'Lax', 'range' => ['none', 'lax', 'strict']],
            'httponly' => ['type' => 'bool', 'default' => true],
            'lifetime' => ['type' => 'integer_eval', 'default' => (3*3600)],
            'domain'   => ['type' => 'string', 'default' => '/']
        ];
        $returnValues = [];
        $ini_file_name = LEPTON_PATH . "/config/lepton.ini.php";
        if (true === file_exists($ini_file_name))
        {
            $config = parse_ini_string(";" . file_get_contents($ini_file_name), true);
            if (isset($config['lepton_cookie']))
            {
                foreach($config['lepton_cookie'] as $key => $value)
                {
                    $returnValues[$key] = $value;
                }
            }
        }

        $oREQUEST = LEPTON_request::getInstance();
        
        foreach ($returnValues as $key => &$val)
        {
            if (isset($vals[$key]))
            {
                $val = $oREQUEST->filterValue(
                    $val,
                    $vals[$key]['type'],
                    $vals[$key]['default'],
                    $vals[$key]['range'] ?? ''
                );
            }
        }
        return $returnValues;
    }
}
