<?php

/*
	hub.php
	-------
	ChatGPT OAuth hub entry point. Routes the authorise request to the last
	Collabit tenant that set the browser's last_found_collabit_server cookie.
*/

$oauth_ui_paths = [
	dirname(__DIR__) . '/functions/oauth/oauth_ui.php',
	'/opt/collabit/functions/oauth/oauth_ui.php'
];
foreach ($oauth_ui_paths as $oauth_ui_path) {
	if (is_file($oauth_ui_path)) {
		require_once $oauth_ui_path;
		break;
	}
}
if (!function_exists('collabit_oauth_status_exit')) {
	http_response_code(422);
	exit("Please sign into Collabit before attempting to connect.");
}

function hub_html_escape(string $value): string {
	return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

function hub_request_uri(): string {
	return $_SERVER['REQUEST_URI'] ?? '/hub.php';
}

function hub_query_string(): string {
	return !empty($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '';
}

function hub_truthy_param(string $name): bool {
	$value = strtolower(trim((string)($_GET[$name] ?? '')));
	return in_array($value, ['1', 'true', 'yes', 'on'], true);
}

function hub_routing_table(): array {
	$routing_table_paths = [
		__DIR__ . '/proxy_routing_table.php',
		dirname(__DIR__) . '/proxy_routing_table.php',
		'/var/www/html/proxy_routing_table.php'
	];
	foreach ($routing_table_paths as $routing_table_path) {
		if (is_file($routing_table_path)) {
			require_once $routing_table_path;
			break;
		}
	}
	if (!class_exists('routing')) {
		return [];
	}
	try {
		$routing_table = routing::get_routing_table();
		return is_array($routing_table) ? $routing_table : [];
	} catch (Throwable $e) {
		return [];
	}
}

function hub_normalise_tenant_input(string $input): string {
	$input = strtolower(trim($input));
	$input = preg_replace('/\s+/', '', $input);
	$input = is_string($input) ? $input : '';
	$input = preg_replace('#^https?://#', '', $input);
	$input = preg_replace('/[?#].*$/', '', is_string($input) ? $input : '');
	$input = trim(is_string($input) ? $input : '', '/');
	if (substr($input, -2) === '/m') {
		$input = substr($input, 0, -2);
	}
	return trim($input, '/');
}

function hub_route_saved_path(array $route): ?string {
	$url = (string)($route['url'] ?? '');
	$parts = parse_url($url);
	$host = strtolower((string)($parts['host'] ?? ($route['host'] ?? '')));
	$path = trim((string)($parts['path'] ?? ('/' . ($route['cust'] ?? ''))), '/');
	if ($host === '' || $path === '') {
		return null;
	}
	return $host . '/' . $path;
}

function hub_route_matches_input(array $route, string $normalised_input): bool {
	$saved_path = hub_route_saved_path($route);
	if ($saved_path === null) {
		return false;
	}
	$url = strtolower((string)($route['url'] ?? ''));
	$url = hub_normalise_tenant_input($url);
	$cust = strtolower((string)($route['cust'] ?? ''));
	return in_array($normalised_input, array_filter([$saved_path, $url, $cust]), true);
}

function hub_resolve_tenant(string $tenant): ?array {
	$normalised = hub_normalise_tenant_input($tenant);
	if ($normalised === '' || preg_match('/^[a-z0-9.-]+(?:\/[a-z0-9._~-]+)?$/', $normalised) !== 1) {
		return null;
	}

	$matches = [];
	foreach (hub_routing_table() as $route) {
		if (!is_array($route)) {
			continue;
		}
		if (hub_route_matches_input($route, $normalised)) {
			$matches[] = $route;
		}
	}

	if (count($matches) !== 1) {
		return null;
	}

	$saved_path = hub_route_saved_path($matches[0]);
	if ($saved_path === null) {
		return null;
	}

	return [
		'url' => rtrim((string)$matches[0]['url'], '/'),
		'saved_path' => $saved_path
	];
}

function hub_split_account_input(string $tenant, string $username): array {
	$tenant = trim($tenant);
	$username = trim($username);
	$slash_position = strrpos($username, '/');
	if ($slash_position !== false) {
		$prefix = substr($username, 0, $slash_position);
		$suffix = substr($username, $slash_position + 1);
		if (trim($prefix) !== '' && trim($suffix) !== '') {
			$tenant = trim($prefix);
			$username = trim($suffix);
		}
	}
	return [$tenant, $username];
}

function hub_account_value(string $tenant, string $username): string {
	$tenant = trim($tenant);
	$username = trim($username);
	if ($tenant !== '' && $username !== '') {
		return $tenant . '/' . $username;
	}
	return $username;
}

function hub_set_tenant_cookie(string $saved_path): void {
	setcookie('last_found_collabit_server', $saved_path, [
		'expires' => time() + 60 * 60 * 24 * 180,
		'path' => '/',
		'secure' => true,
		'httponly' => false,
		'samesite' => 'Lax'
	]);
}

function hub_redirect_to_route(array $route): void {
	hub_set_tenant_cookie((string)$route['saved_path']);
	header("X-Frame-Options: ALLOW-FROM https://chatgpt.com");
	header("Content-Security-Policy: frame-ancestors https://chatgpt.com");
	header('Location: ' . rtrim((string)$route['url'], '/') . '/authorize.php' . hub_query_string());
	exit;
}

function hub_login_forward_exit(array $route, string $username, string $password): void {
	hub_set_tenant_cookie((string)$route['saved_path']);
	header('Cache-Control: no-store');
	header('Pragma: no-cache');
	header('Content-Type: text/html; charset=UTF-8');
	header("X-Frame-Options: ALLOW-FROM https://chatgpt.com");
	header("Content-Security-Policy: frame-ancestors https://chatgpt.com");

	$action = hub_html_escape(rtrim((string)$route['url'], '/') . '/oauth_login.php' . hub_query_string());
	$username = hub_html_escape($username);
	$password = hub_html_escape($password);
	?>
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Signing in to Collabit</title>
</head>
<body>
	<form id="collabit-oauth-login-forward" method="post" action="<?= $action ?>">
		<input type="hidden" name="username" value="<?= $username ?>">
		<input type="hidden" name="password" value="<?= $password ?>">
		<button type="submit">Continue signing in to Collabit</button>
	</form>
	<script>
		document.getElementById('collabit-oauth-login-forward').submit();
	</script>
</body>
</html>
	<?php
	exit;
}

function hub_tenant_select_exit(?string $error = null, string $tenant_value = '', string $username_value = ''): void {
	$request_uri = hub_html_escape(hub_request_uri());
	$tenant_value = trim($tenant_value);
	$username_value = trim($username_value);
	$account_value = hub_html_escape(hub_account_value($tenant_value, $username_value));
	$tenant_value = hub_html_escape($tenant_value);
	$error_html = '';
	if ($error !== null && $error !== '') {
		$error_html = '<div class="error-message">' . hub_html_escape($error) . '</div>';
	}
	$extra_html = <<<HTML
		<div class="tenant-form">
			<form method="post" action="{$request_uri}">
				<input type="hidden" name="hub_login" value="1">
				<input id="tenant" name="tenant" type="hidden" value="{$tenant_value}">
				<label for="username">Username</label>
				<input class="text-input" id="username" name="username" type="text" autocapitalize="none" autocomplete="username" placeholder="tenant/username" value="{$account_value}">
				<div class="detail" id="tenant_hint">Use the format tenant/username.</div>
				<label for="password">Password</label>
				<input class="text-input" id="password" name="password" type="password" autocomplete="current-password" placeholder="Password">
				{$error_html}
				<div class="inline-actions">
					<button class="btn btn-primary" type="submit">Sign in and connect</button>
					<a class="btn btn-secondary" href="{$request_uri}">I've already signed in</a>
				</div>
			</form>
			<script>
				(function () {
					var username = document.getElementById('username');
					var tenant = document.getElementById('tenant');
					var hint = document.getElementById('tenant_hint');
					if (!username || !tenant || !hint) return;
					function updateTenantHint() {
						var value = username.value.trim();
						var slashAt = value.lastIndexOf('/');
						if (slashAt > 0 && slashAt < value.length - 1) {
							var selectedTenant = value.substring(0, slashAt).trim();
							tenant.value = selectedTenant;
							hint.textContent = 'Tenant selected: ' + selectedTenant;
						} else {
							tenant.value = '';
							hint.textContent = 'Use the format tenant/username.';
						}
					}
					username.addEventListener('input', updateTenantHint);
					updateTenantHint();
				})();
			</script>
		</div>
HTML;

	collabit_oauth_status_exit('Please sign in to Collabit first', 'Enter your Collabit sign-in name as tenant/username, then your password.', [
		'status_code' => 422,
		'status_label' => 'Collabit sign in required',
		'detail' => 'When you type the part before the slash, the hub selects that Collabit tenant for this connection.',
		'primary_label' => '',
		'helper_text' => '',
		'extra_html' => $extra_html
	]);
}

if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') {
	[$tenant, $username] = hub_split_account_input((string)($_POST['tenant'] ?? ''), (string)($_POST['username'] ?? ''));
	$route = hub_resolve_tenant($tenant);
	if ($route !== null) {
		$password = (string)($_POST['password'] ?? '');
		if (isset($_POST['hub_login']) || $username !== '' || $password !== '') {
			if ($username === '' || $password === '') {
				hub_tenant_select_exit('Enter both username and password to sign in, or use the already-signed-in option.', $tenant, $username);
			}
			hub_login_forward_exit($route, $username, $password);
		}
		hub_redirect_to_route($route);
	}
	hub_tenant_select_exit('Tenant not recognised or not enabled for ChatGPT. Enter it as tenant/username, for example using the tenant name from the Collabit URL.', $tenant, $username);
}

$savedPath = $_COOKIE['last_found_collabit_server'] ?? false;

if (hub_truthy_param('show_login') || hub_truthy_param('login_preview')) {
	hub_tenant_select_exit(null, (string)($_GET['tenant'] ?? ''), (string)($_GET['username'] ?? ''));
}

if (!$savedPath) {
	hub_tenant_select_exit();
}

$route = is_string($savedPath) ? hub_resolve_tenant($savedPath) : null;
if ($route === null) {
	hub_tenant_select_exit('The saved Collabit tenant is no longer recognised. Please enter the tenant URL again.', is_string($savedPath) ? $savedPath : '');
}

hub_redirect_to_route($route);

?>
