<?php

/**
 * CGBetterUploads API
 *
 * @package CGBetterUploads
 */

namespace CGBetterUploads;
use JsonSerializable;

/**
 * An object to represent a time limited URL to a file.
 * A time limited URL is an additional URL to a file that has more capabilities, including an expiry, an optional email address,
 * an optional password, and an optional number of downloads.
 *
 * @property-read string $guid The unique guid of this timelimited URL.
 * @property-read string $file_guid The guid of the file that can be downloaded.
 * @property-read string $email The email address that is required in order to download the file using this URL.  May be null
 * @property-read int $max_downloads The maximum number of timest hat this URL can be used to download the file.
 * @property-read string $password_hash The hashed one way password, that the user must enter before downloading this file.  May be null
 * @property-read int $created The unix timestamp of when this record was created.
 * @property-read int $expires The unix timestamp of when this record expires.
 */
class TimeLimitedUpload implements TimeLimitedInterface, JsonSerializable
{
    /**
     * @ignore
     */
    private $_data = [
        'guid'=>null, 'file_guid'=>null, 'email'=>null, 'max_downloads'=>null, 'password_hash'=>null, 'created'=>null, 'expires'=>null
        ];

    /**
     * Create a new timelimited uplaod object
     *
     * @internal
     * @param array $in An array of properties and values.
     */
    public function __construct(array $in)
    {
        foreach( $in as $key => $val ) {
            switch( $key ) {
            case 'guid':
            case 'file_guid':
            case 'password_hash':
            case 'email':
                if( !is_null($val) ) $val = trim($val);
                $this->_data[$key] = $val;
                break;
            case 'max_downloads':
            case 'created':
            case 'expires':
                if( !is_null($val) ) $val = (int) $val;
                $this->_data[$key] = $val;
                break;
            }
        }
    }

    /**
     * @ignore
     */
    public function __get( string $key )
    {
        return $this->get($key);
    }

    /**
     * @ignore
     */
    public function __set( string $key, $val )
    {
        throw new \LogicException("$key is not a settable property of ".__CLASS__);
    }

    /**
     * Get a property value.
     *
     * @param string $key the property name
     * @return mixed
     */
    public function get( string $key )
    {
        switch( $key ) {
        case 'guid':
        case 'file_guid':
        case 'email':
        case 'password_hash':
            if( !is_null($this->_data[$key]) ) return trim($this->_data[$key]);
            return;
        case 'max_downloads':
        case 'created':
        case 'expires':
            if( !is_null($this->_data[$key]) ) return (int) $this->_data[$key];
            return;
        case 'expired':
            return $this->expires > 0 && $this->expires < time();
        default:
            throw new \LogicException("$key is not a gettable property of ".__CLASS__);
        }
    }

    /**
     * Adjust the expiry date of a timelimited url
     *
     * @internal
     * @param int $expires
     * @return TimeLimitedInterface
     */
    public function withExpires(int $expires) : TimeLimitedInterface
    {
        if( $expires < 1 ) throw new \LogicException('Invalid expires passed to '.__METHOD__);
        $obj = clone $this;
        $obj->_data['expires'] = $expires;
        return $obj;
    }

    /**
     * Adjust the email address associated with the object.
     *
     * @internal
     * @param string $email
     * @return TimeLimitedInterface
     */
    public function withEmail(string $email = null) : TimeLimitedInterface
    {
        if( !is_null($email) ) {
            $email = trim($email);
        }
        if( empty($email) ) $email = null;
        if( !empty($email) && !is_email($email) ) throw new \LogicException('Invalid email passed to '.__METHOD__);
        $obj = clone $this;
        $obj->_data['email'] = $email;
        return $obj;
    }

    /**
     * Adjust the password associated with the object.
     * The password must be in plain text, it will be hashed using the password_hash function.
     *
     * @internal
     * @param string $text_password
     * @return TimeLimitedInterface
     */
    public function withPassword(string $text_password = null) : TimeLimitedInterface
    {
        $hash = null;
        if( !is_null($text_password) ) $text_password = trim($text_password);
        if( empty($text_password) ) $text_password = null;
        if( $text_password ) $hash = password_hash($text_password, PASSWORD_BCRYPT);
        $obj = clone $this;
        $obj->_data['password_hash'] = $hash;
        return $obj;
    }

    /**
     * Adjust the guid of the file that this URL is pointing to.
     *
     * @internal
     * @param string $guid
     * @return TimeLimitedInterface
     */
    public function withFileGuid(string $guid) : TimeLimitedInterface
    {
        $guid = trim($guid);
        if( !$guid ) throw new \LogicException('Invalid File guid passed to '.__METHOD__);
        $obj = clone $this;
        $obj->_data['file_guid'] = $guid;
        return $obj;
    }

    /**
     * Adjust the maximum number of downloads permitted with this object.
     *
     * @internal
     * @param int $val
     * @return TimeLimitedInterface
     */
    public function withMaxDownloads(int $val = null) : TimeLimitedInterface
    {
        if( !is_null($val) && $val < 0 ) throw new \InvalidArgumentException('Invalid value for max downloads');
        $obj = clone $this;
        $obj->_data['max_downloads'] = $val;
        return $obj;
    }

    /**
     * @ignore
     */
    public function JsonSerialize()
    {
        return $this->_data;
    }

    /**
     * @ignore
     */
    public function as_array() : array
    {
        return $this->_data;
    }
} // class
