<?php
/**
 * app/models/token.php
 *
 * This class is used for the manipulation of the tokens database table.
 *
 * @version 3.0
 * @author  Joey Kimsey <Joey@thetempusproject.com>
 * @link    https://TheTempusProject.com
 * @license https://opensource.org/licenses/MIT [MIT LICENSE]
 */
namespace TheTempusProject\Models;

use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Canary\Bin\Canary as Debug;
use TheTempusProject\Classes\DatabaseModel;
use TheTempusProject\Bedrock\Classes\Config;
use TheTempusProject\TheTempusProject as App;

class Token extends DatabaseModel {
    public $tableName = 'tokens';
    public $modelVersion = '1.0';
    public $configName = 'api';
    public $databaseMatrix = [
        [ 'name', 'varchar', '128' ],
        [ 'token_type', 'varchar', '8' ],
        [ 'notes', 'text', '' ],
        [ 'token', 'varchar', '64' ],
        [ 'secret', 'varchar', '256' ],
        [ 'createdAt', 'int', '10' ],
        [ 'createdBy', 'int', '10' ],
        [ 'expiresAt', 'int', '10' ],
    ];
    public $permissionMatrix = [
        'addAppToken' => [
            'pretty' => 'Add Application Tokens',
            'default' => false,
        ],
        'addAppToken' => [
            'pretty' => 'Add Personal Tokens',
            'default' => false,
        ],
    ];
    public $configMatrix = [
        'apiAccessApp' => [
            'type' => 'radio',
            'pretty' => 'Enable Api Access for Personal Tokens.',
            'default' => true,
        ],
        'apiAccessPersonal' => [
            'type' => 'radio',
            'pretty' => 'Enable Api Access for Personal Tokens.',
            'default' => true,
        ],
        'AppAccessTokenExpiration' => [
            'type' => 'text',
            'pretty' => 'How long before app tokens expire (in seconds)',
            'default' => 2592000,
        ],
        'UserAccessTokenExpiration' => [
            'type' => 'text',
            'pretty' => 'How long before user tokens expire (in seconds)',
            'default' => 604800,
        ],
    ];

    public function create( $name, $note, $token_type = 'app' ) {
        if ( 'app' == $token_type ) {
            $expiration = Config::getValue( 'api/AppAccessTokenExpiration' );
            if ( empty( $expiration ) ) {
                $expiration = $this->configMatrix['AppAccessTokenExpiration']['default'];
            }
        } else {
            $expiration = Config::getValue( 'api/UserAccessTokenExpiration' );
            if ( empty( $expiration ) ) {
                $expiration = $this->configMatrix['UserAccessTokenExpiration']['default'];
            }
        }
        $expireTime = time() + $expiration;
        
        $fields = [
            'name' => $name,
            'notes' => $note,
            'token_type' => $token_type,
            'createdBy' => App::$activeUser->ID,
            'createdAt' => time(),
            'expiresAt' => $expireTime,
            'token' => generateToken(),
            'secret' => generateRandomString(256),
        ];
        if ( self::$db->insert( $this->tableName, $fields ) ) {
            return true;
        }
        return false;
    }

    public function findOrCreateUserToken( $user_id, $refresh = false ) {
        $test = $this->findUserToken( $user_id );
        if ( ! empty( $test ) ) {
            if ( ! empty( $refresh ) ) {
                $token = $this->refresh( $test->ID, 'user' );
            } else {
                $token = $test->token;
            }
            return $token;
        }

        $expiration = Config::getValue( 'api/UserAccessTokenExpiration' );
        if ( empty( $expiration ) ) {
            $expiration = $this->configMatrix['UserAccessTokenExpiration']['default'];
        }
        $expireTime = time() + $expiration;
        $token = generateToken();
        $fields = [
            'name' => 'Browser Token',
            'notes' => 'findOrCreateUserToken',
            'token_type' => 'user',
            'createdBy' => $user_id,
            'createdAt' => time(),
            'expiresAt' => $expireTime,
            'token' => $token,
            'secret' => generateRandomString(256),
        ];
        if ( self::$db->insert( $this->tableName, $fields ) ) {
            return $token;
        }
        return false;
    }

    public function update( $id, $name, $note, $token_type = 'app' ) {
        $fields = [
            'name' => $name,
            'notes' => $note,
            'token_type' => $token_type,
        ];
        if ( self::$db->update( $this->tableName, $id, $fields ) ) {
            return true;
        }
        return false;
    }
    
    public function refresh( $id, $token_type = 'app' ) {
        if ( 'app' == $token_type ) {
            $expiration = Config::getValue( 'api/AppAccessTokenExpiration' );
            if ( empty( $expiration ) ) {
                $expiration = $this->configMatrix['AppAccessTokenExpiration']['default'];
            }
        } else {
            $expiration = Config::getValue( 'api/UserAccessTokenExpiration' );
            if ( empty( $expiration ) ) {
                $expiration = $this->configMatrix['UserAccessTokenExpiration']['default'];
            }
        }
        $expireTime = time() + $expiration;
        $token = generateToken();
        
        $fields = [
            'expiresAt' => $expireTime,
            'token' => $token,
        ];
        if ( self::$db->update( $this->tableName, $id, $fields ) ) {
            return $token;
        }
        return false;
    }

    public function findByforwardedUrl( $url ) {
        if ( !Check::url( $url ) ) {
            Debug::warn( "Invalid forwarded_url: $url" );
            return false;
        }
        $routeData = self::$db->get( $this->tableName, [ 'forwarded_url', '=', $url ] );
        if ( !$routeData->count() ) {
            Debug::warn( "Could not find route by forwarded url: $url" );
            return false;
        }
        return $this->filter( $routeData->first() );
    }

    public function findByToken( $token ) {
        $data = self::$db->get( $this->tableName, [ 'token', '=', $token ] );
        if ( ! $data->count() ) {
            return false;
        }
        return $data->first();
    }

    public function findBySecret( $secret ) {
        $data = self::$db->get( $this->tableName, [ 'secret', '=', $secret ] );
        if ( ! $data->count() ) {
            return false;
        }
        return $data->first();
    }
    
    public function findUserToken( $user_id ) {
        $data = self::$db->get( $this->tableName, [ 'createdBy', '=', $user_id, 'AND', 'token_type', '=', 'user' ] );
        if ( ! $data->count() ) {
            return false;
        }
        return $data->first();
    }
}
