<?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_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_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_tenant_select_exit(?string $error = null, string $tenant_value = ''): void {
	$request_uri = hub_html_escape(hub_request_uri());
	$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">
			<div class="inline-actions">
				<a class="btn btn-primary" href="{$request_uri}">I've signed in, continue</a>
			</div>
			<form method="post" action="{$request_uri}">
				<label for="tenant">Or enter your Collabit tenant</label>
				<input class="text-input" id="tenant" name="tenant" type="text" autocapitalize="none" autocomplete="organization" placeholder="yourcompany.collabit.at/yourtenant" value="{$tenant_value}">
				<div class="detail">Use the tenant URL you normally sign in to. The hub only continues if it matches a known Collabit tenant.</div>
				{$error_html}
				<div class="inline-actions">
					<button class="btn btn-primary" type="submit">Continue with this tenant</button>
				</div>
			</form>
		</div>
HTML;

	collabit_oauth_status_exit('Please sign in to Collabit first', 'ChatGPT does not yet know which Collabit tenant to connect to.', [
		'status_code' => 422,
		'status_label' => 'Tenant not selected',
		'detail' => 'Open your normal Collabit tenant in this browser and sign in, or enter the tenant URL below.',
		'primary_label' => '',
		'extra_html' => $extra_html
	]);
}

if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') {
	$tenant = (string)($_POST['tenant'] ?? '');
	$route = hub_resolve_tenant($tenant);
	if ($route !== null) {
		hub_redirect_to_route($route);
	}
	hub_tenant_select_exit('Tenant not recognised or not enabled for ChatGPT. Check the Collabit URL and include the tenant path if there is one.', $tenant);
}

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

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);

?>
