<?php
namespace SabaiApps\Framework\Application;

/**
 * Abstract class representing a request.
 * Provides methods for accessing and manipulating request parameters with type casting and validation.
 */
abstract class AbstractRequest {

	/**
	 * Array of request parameters.
	 *
	 * @var array
	 */
	protected $_params;

	/**
	 * Constructor.
	 * Initializes the request with the given parameters.
	 *
	 * @param array $params Request parameters.
	 */
	protected function __construct( array $params ) {
		$this->_params = $params;
	}

	/**
	 * Returns all request parameters.
	 *
	 * @return array
	 */
	public function getParams() {
		return $this->_params;
	}

	/**
	 * Gets a request variable as a certain PHP type variable.
	 * Performs type casting and optional inclusion/exclusion validation.
	 *
	 * @access protected
	 * @param string     $type The PHP type to cast to (e.g., 'string', 'int', 'bool', etc.).
	 * @param string     $name Parameter name.
	 * @param mixed      $default Default value if the parameter is not set or invalid.
	 * @param array|null $include Optional array of allowed values.
	 * @param array|null $exclude Optional array of disallowed values.
	 * @return mixed
	 */
	protected function _as( $type, $name, $default, ?array $include = null, ?array $exclude = null ) {
		$ret = $default;
		if ( $this->has( $name ) ) {
			$ret = $this->get( $name );
			if ( @settype( $ret, $type ) ) {
				if ( isset( $include ) ) {
					if ( ! in_array( $ret, $include, true ) ) {
						$ret = $default;
					}
				} elseif ( isset( $exclude ) ) {
					if ( in_array( $ret, $exclude, true ) ) {
						$ret = $default;
					}
				}
			} else {
				$ret = $default;
			}
		}

		return $ret;
	}

	/**
	 * Gets a request variable as an array.
	 *
	 * @param string     $name Parameter name.
	 * @param array      $default Default value.
	 * @param array|null $include Optional array of allowed values.
	 * @param array|null $exclude Optional array of disallowed values.
	 * @return array
	 */
	public function asArray( $name, $default = array(), ?array $include = null, ?array $exclude = null ) {
		return $this->_as( 'array', $name, $default, $include, $exclude );
	}

	/**
	 * Gets a request variable as a string.
	 *
	 * @param string     $name Parameter name.
	 * @param string     $default Default value.
	 * @param array|null $include Optional array of allowed values.
	 * @param array|null $exclude Optional array of disallowed values.
	 * @return string
	 */
	public function asStr( $name, $default = '', ?array $include = null, ?array $exclude = null ) {
		return $this->_as( 'string', $name, $default, $include, $exclude );
	}

	/**
	 * Gets a request variable as an integer.
	 *
	 * @param string     $name Parameter name.
	 * @param int        $default Default value.
	 * @param array|null $include Optional array of allowed values.
	 * @param array|null $exclude Optional array of disallowed values.
	 * @return int
	 */
	public function asInt( $name, $default = 0, ?array $include = null, ?array $exclude = null ) {
		return $this->_as( 'integer', $name, $default, $include, $exclude );
	}

	/**
	 * Gets a request variable as a boolean.
	 *
	 * @param string $name Parameter name.
	 * @param bool   $default Default value.
	 * @return bool
	 */
	public function asBool( $name, $default = false ) {
		return $this->_as( 'boolean', $name, $default, null, null );
	}

	/**
	 * Gets a request variable as a float.
	 *
	 * @param string     $name Parameter name.
	 * @param float      $default Default value.
	 * @param array|null $include Optional array of allowed values.
	 * @param array|null $exclude Optional array of disallowed values.
	 * @return float
	 */
	public function asFloat( $name, $default = 0.0, ?array $include = null, ?array $exclude = null ) {
		return $this->_as( 'float', $name, $default, $include, $exclude );
	}

	/**
	 * Checks if a request parameter is present.
	 *
	 * @param string $name Parameter name.
	 * @return bool True if the parameter exists, false otherwise.
	 */
	public function has( $name ) {
		return isset( $this->_params[ $name ] );
	}

	/**
	 * Gets the value of a request parameter.
	 *
	 * @param string $name Parameter name.
	 * @return mixed The value of the parameter, or null if not set.
	 */
	public function get( $name ) {
		return isset( $this->_params[ $name ] ) ? $this->_params[ $name ] : null;
	}

	/**
	 * Sets the value of a request parameter.
	 *
	 * @param string $name Parameter name.
	 * @param mixed  $value Value to set.
	 * @return AbstractRequest
	 */
	public function set( $name, $value ) {
		$this->_params[ $name ] = $value;
		return $this;
	}

	/**
	 * Magic getter for request parameters.
	 * Allows access to parameters as object properties.
	 *
	 * @param string $name Parameter name.
	 * @return mixed
	 */
	public function __get( $name ) {
		return $this->_params[ $name ];
	}

	/**
	 * Magic setter for request parameters.
	 * Allows setting parameters as object properties.
	 *
	 * @param string $name Parameter name.
	 * @param mixed  $value Value to set.
	 */
	public function __set( $name, $value ) {
		$this->_params[ $name ] = $value;
	}

	/**
	 * Magic isset for request parameters.
	 * Allows checking if a parameter is set as an object property.
	 *
	 * @param string $name Parameter name.
	 * @return bool
	 */
	public function __isset( $name ) {
		return isset( $this->_params[ $name ] );
	}

	/**
	 * Magic unset for request parameters.
	 * Allows unsetting a parameter as an object property.
	 *
	 * @param string $name Parameter name.
	 */
	public function __unset( $name ) {
		unset( $this->_params[ $name ] );
	}
}
