<?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
 *
 */

/**
 *  Extended methods for LEPTON_database
 *
 */
trait LEPTON_secure_database
{
    
    static private string $db_key = "";
    
    static private string $default_openssl_method = "";
    
    static private string $default_openssl_iv = "";
    
    static private string $default_openssl_ivlen = "";
    
    static private int $default_openssl_options = 0;
	
    /**
     *  needed for addons using secure_database
     */
    private bool $bOpenSslInstalled = false;
	
    /**
     *  To encrypt any string.
     *  @param  string  $sSource    Any string
     *  @return string  The encrypted string.
     */
    protected function cryptString(string $sSource=""): string|bool
    {
        if( false === $this->bOpenSslInstalled )
        {
            return $sSource;
        }
        return openssl_encrypt(
            $sSource,
            self::$default_openssl_method,
            self::$db_key,
            self::$default_openssl_options,
            self::$default_openssl_iv
        );
    }

    /**
     *  To decrypt any string.
     *  @param  string  $sSource    Any encrypted string.
     *  @return string  The decrypted string.
     */    
    protected function decryptString(string $sSource): string|bool
    {
        if ((false === $this->bOpenSslInstalled) || ("" === $sSource))
        {
            return $sSource;
        }
        
        return openssl_decrypt(
            $sSource,
            self::$default_openssl_method,
            self::$db_key,
            self::$default_openssl_options,
            self::$default_openssl_iv
        );
    }

    /** *********************
     *  [1] Interface functions
     *
     */
    
    /**
     *  Set the method
     *  @param  string $sMethodName A valid Method.
     *  @return void
     */
    public function setOpenSSLMethod(string $sMethodName): void
    {
        self::$default_openssl_method = $sMethodName;
    }

    /**
     *  Set the iv
     *  @param  string $sNewIV A valid iv (16 bytes!)
     *  @return void
     */
    public function setOpenSSLIv(string $sNewIV): void
    {
        self::$default_openssl_iv = $sNewIV;
    }

    
    /** *********************
     *  [2] New class methods
     *
     */
     
    /**
     *	Execute a SQL query and return the first row of the result array
     *
     *	@param	string	$SQL Any SQL-Query or statement
     *
     *	@return	string|null Value of the table-field or NULL for error
     */
    public function secure_get_one(string $SQL): string|null
    {
        $result = $this->get_one($SQL);
        return (is_null($result))
            ? NULL
            : $this->decryptString( (string)$result )
            ;
    }

    /**
     *	Public "shortcut" for executing a single mySql-query without passing values.
     *
     *	@param	string	$aQuery     A valid mySQL query.
     *	@param	bool	$bFetch     Fetch the result - default is false.
     *	@param	array	$aStorage   A storage array for the fetched results. Pass by reference!
     *	@param	bool	$bFetchAll  Try to get all entries. Default is true.
     *  @param	array	$aListOfFields  A linear array with the fieldnames to de-crypt.
	 *
	 *	@return	bool	False if fails, otherwise true.
	 *
	 * @example
     *	$results_array = array();       
     *  $this->db_handle->execute_query( 
	 * 			"SELECT * from ".TABLE_PREFIX."pages WHERE page_id = ".$page_id." ",
	 *			true, 
	 *			$results_array, 
	 *			false,
	 *          array('modify_by') 
	 *	);
     *   	 
     *
     */
    public function secure_execute_query(
        string $aQuery = "",
        bool $bFetch = false,
        array &$aStorage = array(),
        bool $bFetchAll = true,
        array $aListOfFields = array()
    ): bool
    {
        if (!is_array($aListOfFields))
		{
		    LEPTON_tools::display("REQUIRED list of fieldnames must be an array!", "div", "ui message red");
		    return false;
		}
		
		$result = $this->execute_query(
		    $aQuery,
		    $bFetch,
		    $aStorage,
		    $bFetchAll
		);

        if (false === $result)
		{
            LEPTON_tools::display($this->get_error(), "div", "ui message red");
		    return false;
		}

        if (true === $bFetchAll)
		{
            foreach ($aStorage as &$ref)
		    {
                foreach ($aListOfFields as $sFieldname)
		        {
                    if (isset($ref[$sFieldname]))
		            {
                        $ref[$sFieldname] = $this->decryptString( (string)$ref[$sFieldname]);
		            }
		        }
		    }
		} else {
            foreach ($aListOfFields as $sFieldname)
		    {
                if (isset($aStorage[$sFieldname]))
		        {
                    $aStorage[$sFieldname] = $this->decryptString( (string)$aStorage[$sFieldname]);
		        }
		    }
		}
		
		return true;
    }

    /**
     *	Public function to build and execute a mySQL query direct.
     *	Use this function/method for update and insert values only.
     *	As for a simple select you can use "prepare_and_execute" above.
     *
     *	@param	string	$type           A "job"-type: this time only "update" and "insert" are supported.
     *	@param	string	$table_name     A valid tablename (incl. table-prefix).
     *	@param	array	$table_values   An array within the table-field-names and values. Pass by reference!
     *	@param	string	$condition      An optional condition for "update" - this time a simple string.
     *  @param	array	$aListOfFields  A linear array with the fieldnames to crypt.
     *  @param  bool $display_query     An optional value to display query as a string for development (only)	 
     *
     *	@return	int	False if fails, otherwise true.
     *
     */
    public function secure_build_and_execute(
        string $type,
        string $table_name,
        array $table_values = [] ,
        string $condition = "",
        array $aListOfFields = [],
		bool $display_query = false
    ): int
    {

        if (!is_array($aListOfFields))
		{
		    LEPTON_tools::display("REQUIRED list of fieldnames must be an array!", "div", "ui message red");
		    return 0;
		}

        foreach ($aListOfFields as $sName)
    	{
            if (isset($table_values[$sName]))
    	    {
    	        $table_values[ $sName ] = $this->cryptString( (string)$table_values[$sName]);
    	    }
    	}
    	
    	return $this->build_and_execute(
    	    $type,
    	    $table_name,
    	    $table_values,
    	    $condition,
			$display_query
    	); 
    }
	
    /**
     *  Handle static calls
     *
     *  @example
     *          // crypt
     *          LEPTON_database::cryptString( "aladin" );
     *      or
     *          // decrypt
     *          LEPTON_database::decryptstring( $sAnyEncrypted );
     *      or
     *          // ERROR! unknown/undeclared method
     *          LEPTON_database::super(); // THIS WILL THROW AN ERROR MESSAGE! 
     */
    public static function __callStatic($method, $args)
    {
        switch (strtolower($method))
        {
            case 'cryptstring':
                return self::$instance->cryptString((string)$args[0]);
                break;
                
            case 'decryptstring':
                return self::$instance->decryptString((string)$args[0]);
                break;
                
            default:
                echo LEPTON_tools::display("Static call to unknown method!", "pre", "ui message red");
                return NULL;
                break;
        }
    }	
}
