<?php

/*
	Setup
*/
define('SRC', __DIR__ . '/../src');
define('DATABASE_PATH',  __DIR__ . '/../database');
require_once SRC . '/bootstrap.php';

$r = new Router;

/*
	Home
*/
$r->get('/', function() {
	if (user()) redirect('/world');
	echo render('layouts/basic', ['view' => 'pages/hello']);
});

/*
	Auth
*/
$r->get('/register', function() {
	guest_only();
	echo render('layouts/basic', ['view' => 'pages/auth/register']);
});

$r->post('/register', function() {
	guest_only();
	csrf_ensure();

	$errors = [];

	$u = trim($_POST['u'] ?? '');
	$e = trim($_POST['e'] ?? '');
	$p = $_POST['p'] ?? '';

	/*
		A username is required.
		A username must be at least 3 characters long and at most 18 characters long.
		A username must contain only alphanumeric characters and spaces.
	*/
	if (empty($u) || strlen($u) < 3 || strlen($u) > 18 || !ctype_alnum(str_replace(' ', '', $u))) {
		$errors['u'][] = 'Username is required and must be between 3 and 18 characters long and contain only
			alphanumeric characters and spaces.';
	}

	/*
		An email is required.
		An email must be at most 255 characters long.
		An email must be a valid email address.
	*/
	if (empty($e) || strlen($e) > 255 || !filter_var($e, FILTER_VALIDATE_EMAIL)) {
		$errors['e'][] = 'Email is required must be a valid email address.';
	}

	/*
		A password is required.
		A password must be at least 6 characters long.
	*/
	if (empty($p) || strlen($p) < 6) {
		$errors['p'][] = 'Password is required and must be at least 6 characters long.';
	}

	/*
		A username must be unique.
	*/
	if (User::username_exists($u)) {
		$errors['u'][] = 'Username is already taken.';
	}

	/*
		An email must be unique.
	*/
	if (User::email_exists($e)) {
		$errors['e'][] = 'Email is already taken.';
	}

	// If there are errors at this point, send them to the page with errors flashed.
	if (!empty($errors)) {
		$GLOBALS['form-errors'] = $errors;
		echo render('layouts/basic', ['view' => 'pages/auth/register']);
		exit;
	}

	if (User::create($u, $e, $p) === false) error_response(400);

	$_SESSION['user'] = serialize(User::find($u));
	Wallet::create(user()->id);
	redirect('/character/create-first');
});

$r->get('/login', function() {
	guest_only();
	echo render('layouts/basic', ['view' => 'pages/auth/login']);
});

$r->post('/login', function() {
	guest_only();
	csrf_ensure();

	$errors = [];

	$u = trim($_POST['u'] ?? '');
	$p = $_POST['p'] ?? '';

	if (empty($u)) $errors['u'][] = 'Username is required.';
	if (empty($p)) $errors['p'][] = 'Password is required.';

	// If there are errors at this point, send them to the page with errors flashed.
	if (!empty($errors)) {
		$GLOBALS['form-errors'] = $errors;
		echo render('layouts/basic', ['view' => 'pages/auth/login']);
		exit;
	}

	$user = User::find($u);
	if ($user === false || !$user->check_password($p)) {
		$errors['x'][] = 'Invalid username or password.';
		$GLOBALS['form-errors'] = $errors;
		echo render('layouts/basic', ['view' => 'pages/auth/login']);
		exit;
	}

	$_SESSION['user'] = serialize($user);

	if ($_POST['remember'] ?? false) {
		$session = Session::create($user->id, strtotime('+30 days'));
		if ($session === false) error_response(400);
		set_cookie('remember_me', $session->token, $session->expires);
	}

	if ($user->char_count() === 0) {
		redirect('/character/create-first');
	} elseif (!change_user_character($user->char_id)) {
		echo "failed to change user character (aclp)";
		error_response(999);
	}

	redirect('/');
});

$r->post('/logout', function() {
	csrf_ensure();
	Session::delete(user()->id);
	unset($_SESSION['user']);
	set_cookie('remember_me', '', 1);
	redirect('/');
});

$r->get('/debug/logout', function() {
	Session::delete(user()->id);
	unset($_SESSION['user']);
	set_cookie('remember_me', '', 1);
	redirect('/');
});

/*
	Characters
*/
$r->get('/characters', function() {
	auth_only_and_must_have_character();

	$GLOBALS['active_nav_tab'] = 'chars';
	//echo page('chars/list', ['chars' => user()->char_list()]);
});

$r->post('/characters', function() {
	auth_only_and_must_have_character();
	csrf_ensure();

	$GLOBALS['active_nav_tab'] = 'chars';

	$char_id = (int) ($_POST['char_id'] ?? 0);
	$action = $_POST['action'] ?? '';

	// If the character ID is not a number, or the action is not a string, return a 400.
	if (!is_numeric($char_id) || !is_string($action)) error_response(400);

	// If the character ID is 0, return to the list.
	if ($char_id === 0) {
		flash('alert_character_list_1', ['', 'No character selected.']);
		redirect('/characters');
	}

	// If the action is not one of the allowed actions, return a 400.
	if (!in_array($action, ['select', 'delete'])) error_response(400);

	// If the action is to select a character, change the user's selected character.
	if ($action === 'select') {
		// If the character ID is the current character, do nothing.
		if ($char_id === user()->char_id || $char_id === 0) {
			flash('alert_character_list_1', ['info', 'You are already using <b>' . char()->name . '</b>.']);
			redirect('/characters');
		}

		if (!Character::belongs_to($char_id, user()->id)) error_response(999);

		change_user_character($char_id);

		flash('alert_character_list_1', ['success', 'Switched to <b>' . char()->name . '</b>!']);
	}

	// If the action is to delete a character, move to the confirmation page.
	if ($action === 'delete') {
		if (!Character::belongs_to($char_id, user()->id)) error_response(999);

		//echo page('chars/delete', ['char' => Character::find($char_id)]);
		exit;
	}

	redirect('/characters');
});

$r->get('/character/create-first', function() {
	auth_only();

	$GLOBALS['active_nav_tab'] = 'chars';

	// If the user already has a character, redirect them to the main page.
	if (user()->char_count() > 0) redirect('/');

	//echo page('chars/first');
});

$r->post('/character/create', function() {
	auth_only(); csrf_ensure();

	$GLOBALS['active_nav_tab'] = 'chars';

	$errors = [];

	$name = trim($_POST['n'] ?? '');

	/*
		A name is required.
		A name must be between 3 and 18 characters.
		A name must contain only alphanumeric characters and spaces.
	*/
	if (empty($name) || strlen($name) < 3 || strlen($name) > 18 || !ctype_alnum(str_replace(' ', '', $name))) {
		$errors['n'][] = 'Name is required and must be between 3 and 18 characters long and contain only alphanumeric characters and spaces.';
	}

	/*
		A character's name must be unique.
	*/
	if (Character::name_exists($name)) $errors['n'][] = 'Name is already taken.';

	// If there are errors at this point, send them to the page with errors flashed.
	if (!empty($errors)) {
		$GLOBALS['form-errors-create-character'] = $errors;

		if (isset($_POST['first']) && $_POST['first'] === 'true') {
			// If this is the first character, return to the first character creation page.
			//echo page('chars/first');
			exit;
		} else {
			// If this is not the first character, return to the character list page.
			//echo page('chars/list', ['chars' => user()->char_list()]);
			exit;
		}
	}

	if (($char = Character::create(user()->id, $name)) === false) error_response(400);

	// Create the auxiliary tables
	$char->create_location();
	$char->create_gear();

	// Award the Adventurer title.
	$char->award_title(1);

	// Set the character as the user's selected character
	change_user_character($char->id);

	flash('alert_character_list_1', ['success', 'Character <b>' . $name . '</b> created!']);
	redirect('/characters');
});

$r->post('/character/delete', function() {
	auth_only_and_must_have_character();
	csrf_ensure();

	$char_id = (int) ($_POST['char_id'] ?? 0);

	// If the character ID is not a number, return a 400.
	if (!is_numeric($char_id)) error_response(400);

	// Ensure the character ID is valid and belongs to the user.
	if (!Character::belongs_to($char_id, user()->id)) error_response(999);

	$char = Character::find($char_id);

	// Confirm the name matches the name of the character. CASE SENSITIVE.
	if ($char['name'] !== trim($_POST['n'] ?? '')) {
		flash('alert_character_list_1', ['danger', 'Failed to delete <b>' . $char['name'] . '</b>. Name confirmation did not match.']);
		redirect('/characters');
	}

	// Delete the character
	Character::delete($char_id);

	// If the character being deleted is the currently selected character, select the first character.
	if (user()->char_id === $char_id) {
		$chars = user()->char_list();
		if (count($chars) > 0) change_user_character($chars[0]['id']);
	}

	flash('alert_character_list_1', ['danger', 'Character <b>' . $char['name'] . '</b> deleted.']);
	redirect('/characters');
});

/*
	World
*/
$r->get('/world', function() {
	auth_only_and_must_have_character();
	echo render('layouts/game');
});

$r->post('/move', function() {
	/*
		This endpoint is used to move the character around the world. The client sends a POST request with the direction
		they want to move the character. The server will update the character's position in the database and return the
		new position to the client.

		We should only be using this endpoint as an AJAX request from the world page. Since we don't need all the character's
		data to move them, we can just get and update their lcoation using the user's currently selected character ID.
	*/

	ajax_only(); auth_only(); csrf_ensure();

	define('directions', [
		[0, -1], // Up
		[0, 1],  // Down
		[-1, 0], // Left
		[1, 0]   // Right
	]);

	// direction must exist
	$d = (int) $_POST['direction'] ?? -1;

	// Update the character's position
	// 0 = up, 1 = down, 2 = left, 3 = right
	$x = location('x');
	$y = location('y');

	if (isset(directions[$d])) {
		$x += directions[$d][0];
		$y += directions[$d][1];
	} else {
		error_response(999);
	}

	$r = live_db()->query('UPDATE char_locations SET x = :x, y = :y WHERE char_id = :c', [
		':x' => $x,
		':y' => $y,
		':c' => user()->char_id
	]);

	if ($r === false) throw new Exception('Failed to move character. (wcmp)');

	json_response(['x' => $x, 'y' => $y]);
});

/*
	UI
*/
$r->get('/ui/stats', function() {
	ui_guard();
	echo c_profile_stats(char());
});

/*
	Router
*/
// [code, handler, params]
stopwatch_start('router');
$l = $r->lookup($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']);
stopwatch_stop('router');

stopwatch_start('handler');
if ($l['code'] !== 200) error_response($l['code']);
$l['handler'](...$l['params'] ?? []);
stopwatch_stop('handler');

/*
	Cleanup
*/
clear_flashes();
