<?php
/**
 * Main handler for the client.
 */

date_default_timezone_set('UTC');


/*
 * Valid combinations of HTTP verb and URI. Code iterates through these and takes the FIRST match, so
 * put the longest ones first.
 *
 * Almost all requests will be GET. We only POST to the current location to force a page reload (not from
 * browser cache). See https://stackoverflow.com/questions/55127650/location-reloadtrue-is-deprecated
 */
$validPatterns=array(
		
		//get shared resources not under access control, like templates of files for upload
        'GET /resources/[^/]+'=>'getResource',
        'POST /resources/[^/]+'=>'getResource',

		//get by property, e.g, /plate/barcode/90a6
        'GET /[A-Za-z]+/name/[A-Za-z0-9]+'=>'getByName',
        'POST /[A-Za-z]+/name/[A-Za-z0-9]+'=>'getByName',

		//get by property, e.g, /plate/barcode/90a6
        'GET /[A-Za-z]+/[A-Za-z0-9]+/[A-Za-z0-9]+'=>'getByProperty',
        'POST /[A-Za-z]+/[A-Za-z0-9]+/[A-Za-z0-9]+'=>'getByProperty',

		//create new record, e.g., /plate/create
        'GET /[A-Za-z]+/create'=>'create',
        'POST /[A-Za-z]+/create'=>'create',

		//get by database ID, e.g, /plate/1234
        'GET /[A-Za-z]+/[0-9]+'=>'getById',
        'POST /[A-Za-z]+/[0-9]+'=>'getById',

		//get all, e.g., /plate - pagination in request parameters
        'GET /[A-Za-z]+'=>'getAll',
        'POST /[A-Za-z]+'=>'getAll',
);

//page defaults
$hasConfig=false;

//define class autoloader
require '../vendor/autoload.php';

//determine request method and URI
$wwwroot=config::getWwwRoot();
$method=strtoupper($_SERVER['REQUEST_METHOD']);
$uri=rtrim(strtok($_SERVER['REQUEST_URI'],'?'),'/');
$uriArgs=explode('/', substr($uri,1));
$req=$method.' '.$uri;

//get request parameters
$parameters=array();
if('GET'==$method){
    $parameters=$_GET;
} else if('POST'==$method){
    //We're POSTing to force reload - see ui.js forceReload()
    $parameters=[];
} else {
	//Shouldn't happen in the client
	//parse_str(file_get_contents("php://input"),$parameters);
	exit;
}

//connect to the database
database::connect();

session::init(new PhpSession());
$userConfig=userconfig::getAll();

if(!session::isLoggedIn()){
	if(shibboleth::isAuthenticated()){
		try {
			shibboleth::setUserToIceBearSession();
		} catch (NotFoundException $e){
			include 'templates/core/login/linkorcreate.php';
			exit();
		}
	} else {
		include 'templates/core/login/login.php';
		exit();
	}
}
$lastLogin=session::get('lastlogin');
if(!$lastLogin){
	$userId=session::getUserId();
	$now=baseuser::setLastLogin($userId);
	session::set('lastlogin',$now);
	database::commit(); //This happens during a GET, which aborts the transaction...
	database::begin();  //so commit and start a new one
}

if(!empty($_COOKIE['urlAfterAuth'])){
	setcookie('urlAfterAuth', '', time()-3600, '/');
	header('Location: '.$_COOKIE['urlAfterAuth']);
}

//Handle homepage, default homepage, homepage Javascript
if('homepagebricks.js'===$uriArgs[0]){
    include $wwwroot.'/client/templates/core/homepage/homepagebricks.php';
    exit();
} else if(0==count($uriArgs) || ''==$uriArgs[0]){
	$objectType="homepage";
	include $wwwroot.'/client/templates/core/homepage/homepage.php';
	exit;
} else if(session::isAdmin() && 2<=count($uriArgs) && 'defaulthomepage'==$uriArgs[1]){
	$isDefaultHomepage=true;
	include $wwwroot.'/client/templates/core/homepage/homepage.php';
	exit;
}

//Now get the appropriate page
//do action
$objectType=null;
$action=null;

$keys=array_keys($validPatterns);
$numPatterns=count($keys);
$uriMatchedPattern=false;
for($i=0;$i<$numPatterns;$i++){
	$pattern=$keys[$i];
	if(preg_match('|^'.$pattern.'/?$|', $req)){
		$action=$validPatterns[$pattern];
		$objectType=$uriArgs[0];
		$uriMatchedPattern=true;
	}
}



try {
	if(!$uriMatchedPattern){
		throw new BadRequestException('Request '.$uri.' did not match a recognised pattern');
	}
	
	//Handle public resource files separately, doesn't involve a class.
	if("getResource"==$action){
		$filename=$uriArgs[1];
		
		$path=realpath(dirname(__FILE__).DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.$filename);
		$safeRoot=dirname(__FILE__).DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR;
		if (!str_starts_with($path, $safeRoot)){
			throw new NotFoundException('No file called '.$filename.' found in public resources folder');
		}
		$path=__DIR__.'/resources/'.$filename;
		if(!file_exists($path)){
			throw new NotFoundException('No file called '.$filename.' found in public resources folder');
		}
		$handle=@fopen($path,'r');
		if(!$handle){
			throw new ServerException('Could not open public resource '.$filename);
		}
		header("Content-Type: application/force-download");
		@fpassthru($handle);
		@fclose($handle);
		exit;
	}
	
	//echo $action."!".$objectType."!";
	if(!$action || !class_exists($objectType) ){
		throw new BadRequestException('Class or method does not exist');
	}
	$helpTexts=forward_static_call_array(array($objectType, 'getFieldHelpTexts'),array());
	$fieldValidations=forward_static_call_array(array($objectType, 'getFieldValidations'),array());
	$validationPatterns=validator::getValidationPatterns();
	switch($action){
		case 'getById':
			$id=$uriArgs[1];
			$args=array($id, $parameters);
			$data=sanitize(forward_static_call_array(array($objectType, 'getById'), $args));
			if(!$data){ throw new NotFoundException('That item does not exist, or you do not have permission to see it'); }
			$canUpdate=forward_static_call_array(array($objectType, 'canUpdate'), $args);
			$pageType='view';
			break;
		case 'getByName':
			$args=array(urldecode($uriArgs[2]), $parameters);
			$data=sanitize(forward_static_call_array(array($objectType, 'getByName'), $args));
			$canUpdate=forward_static_call_array(array($objectType, 'canUpdate'), $args);
			$pageType='view';
			break;
		case 'getByProperty':
			$args=array($uriArgs[1],urldecode($uriArgs[2]), $parameters);
			$data=sanitize(forward_static_call_array(array($objectType, $action), $args));
			$pageType='list';
			break;
		case 'getAll':
			$args=array($parameters);
			$data=sanitize(forward_static_call_array(array($objectType, $action), $args));
			$pageType='list';
			break;
		case 'create':
			$args=array();
			$canCreate=forward_static_call_array(array($objectType, 'canCreate'), $args);
			if(!$canCreate){
				throw new ForbiddenException('You do not have permission to create that.');
			}
			$pageType='create';
			break;
	}

	if('view'==$pageType){
		$data['objecttype']=$objectType;
	}
	
	if(file_exists(__DIR__.'/templates/core/'.$objectType.'/'.$pageType.'.php')){
		include_once __DIR__.'/templates/core/'.$objectType.'/'.$pageType.'.php';
	} else {
		include_once __DIR__.'/templates/model/'.$objectType.'/'.$pageType.'.php';
	}
	
} catch(Exception $ex){
	$responseCode=$ex->getCode();
	http_response_code($responseCode);
//	include $wwwroot.'/client/templates/core/_error/'.$responseCode.'.php';
	include 'templates/core/_error/'.$responseCode.'.php';
}

function sanitize($data){
	if(is_array($data)){
		foreach($data as $k=>&$v){
			if(is_array($v)){
				$v=sanitize($v);
			} else {
                if(is_null($v)){ $v=''; }
                if(PHP_MAJOR_VERSION>=8 && PHP_MINOR_VERSION>=1){
                    $v=htmlspecialchars($v, ENT_QUOTES); //Needed for >=PHP8.1 - see docs
                } else {
                    $v=htmlspecialchars($v);
                }
			}
		}
	}
	return $data;
}


/**
 * @throws ForbiddenException
 */
function requireAdmin(){
	if(!session::isAdmin()){
		throw new ForbiddenException('This is for administrators only.');
	}
}

function redirectToView($objectType, $id){
	header('Location: /'.$objectType.'/'.$id);
}
