<?php
/**
 * Nexora VE Networks — aislamiento de reinos para WebEngine CMS 1.2.6.
 */

class NexoraRealm {

	const SESSION_PUBLIC = 'nexora_public_realm';
	const SESSION_PUBLIC_EXPLICIT = 'nexora_public_realm_explicit';
	const SESSION_ACCOUNT = 'nexora_account_realm';
	const SESSION_OPERATIONAL = 'nexora_operational_realm';

	private static $_config;
	private static $_runtimeRealm;
	private static $_bootstrapped = false;

	private static function _loadConfig() {
		if(is_array(self::$_config)) return self::$_config;

		$file = __PATH_CONFIGS__ . 'nexora.realms.php';
		if(!is_file($file) || !is_readable($file)) {
			throw new Exception('No se pudo cargar la configuración de reinos de Nexora.');
		}

		$config = include($file);
		if(!is_array($config) || !isset($config['realms']) || !is_array($config['realms'])) {
			throw new Exception('La configuración de reinos de Nexora no es válida.');
		}

		self::$_config = $config;
		return self::$_config;
	}

	public static function enabled() {
		$config = self::_loadConfig();
		return !empty($config['enabled']);
	}

	public static function ids() {
		$config = self::_loadConfig();
		return array_keys($config['realms']);
	}

	public static function exists($realm) {
		$config = self::_loadConfig();
		return is_string($realm) && array_key_exists(strtolower($realm), $config['realms']);
	}

	public static function validate($realm) {
		$realm = is_string($realm) ? strtolower(trim($realm)) : '';
		if(!self::exists($realm)) {
			throw new InvalidArgumentException('Selecciona un reino válido: Legado o Seasonal.');
		}
		return $realm;
	}

	public static function defaultRealm() {
		$config = self::_loadConfig();
		$default = isset($config['default']) ? strtolower($config['default']) : '';
		if(!self::exists($default)) {
			$ids = self::ids();
			$default = reset($ids);
		}
		return $default;
	}

	public static function sharedRegistrationEnabled() {
		$config = self::_loadConfig();
		return isset($config['shared_registration'])
			&& is_array($config['shared_registration'])
			&& !empty($config['shared_registration']['enabled']);
	}

	public static function registrationRealms() {
		$config = self::_loadConfig();
		$configured = isset($config['shared_registration']['realms'])
			&& is_array($config['shared_registration']['realms'])
			? $config['shared_registration']['realms']
			: self::ids();

		$realms = array();
		foreach($configured as $realm) {
			$realm = self::validate($realm);
			if(!in_array($realm, $realms, true)) $realms[] = $realm;
		}

		if(count($realms) < 2) {
			throw new RuntimeException('El registro compartido necesita al menos dos reinos válidos.');
		}

		return $realms;
	}

	public static function registrationPrimaryRealm() {
		$config = self::_loadConfig();
		$primary = isset($config['shared_registration']['primary_realm'])
			? $config['shared_registration']['primary_realm']
			: self::defaultRealm();
		$primary = self::validate($primary);
		if(!in_array($primary, self::registrationRealms(), true)) {
			throw new RuntimeException('El reino principal no pertenece al registro compartido.');
		}
		return $primary;
	}

	public static function platformEnabled() {
		if(class_exists('NexoraSecurity')) return (bool)NexoraSecurity::config('platform_enabled', false);
		$config = self::_loadConfig();
		return !empty($config['platform']['enabled']);
	}

	public static function platformDatabase() {
		if(class_exists('NexoraSecurity')) return (string)NexoraSecurity::config('platform_database', 'nexora_platform');
		$config = self::_loadConfig();
		return isset($config['platform']['database']) ? (string)$config['platform']['database'] : 'nexora_platform';
	}

	public static function hasOperationalRealm() {
		return session_status() === PHP_SESSION_ACTIVE
			&& isset($_SESSION[self::SESSION_OPERATIONAL])
			&& self::exists($_SESSION[self::SESSION_OPERATIONAL]);
	}

	public static function operationalRealm() {
		if(self::hasOperationalRealm()) return strtolower($_SESSION[self::SESSION_OPERATIONAL]);
		return self::hasAccountRealm() ? self::accountRealm() : self::publicRealm();
	}

	public static function selectOperational($realm) {
		$realm = self::validate($realm);
		if(session_status() !== PHP_SESSION_ACTIVE) throw new RuntimeException('No se pudo fijar el contexto operativo.');
		$_SESSION[self::SESSION_OPERATIONAL] = $realm;
		self::$_runtimeRealm = $realm;
		return $realm;
	}

	public static function clearOperationalRealm() {
		if(session_status() === PHP_SESSION_ACTIVE) unset($_SESSION[self::SESSION_OPERATIONAL]);
	}

	public static function bootstrap() {
		if(self::$_bootstrapped) return;
		self::$_bootstrapped = true;

		if(!self::enabled()) return;

		if(isset($_GET['realm']) && self::exists($_GET['realm'])) {
			$remember = !(defined('access') && access === 'cron');
			self::selectPublic($_GET['realm'], $remember);
		}
	}

	public static function selectPublic($realm, $remember=true) {
		$realm = self::validate($realm);
		self::$_runtimeRealm = $realm;

		if($remember && session_status() === PHP_SESSION_ACTIVE && !self::hasAccountRealm()) {
			$_SESSION[self::SESSION_PUBLIC] = $realm;
			$_SESSION[self::SESSION_PUBLIC_EXPLICIT] = true;
		}

		return $realm;
	}

	public static function requirePosted($field='nexora_realm') {
		if(!isset($_POST[$field])) {
			throw new InvalidArgumentException('Selecciona Legado o Seasonal antes de continuar.');
		}
		$realm = self::validate($_POST[$field]);
		self::_assertAccountRealm($realm);
		return self::selectPublic($realm, true);
	}

	public static function requireQuery($field='realm') {
		if(!isset($_GET[$field])) {
			throw new InvalidArgumentException('El enlace no identifica el reino de la cuenta. Solicita uno nuevo.');
		}
		$realm = self::validate($_GET[$field]);
		self::_assertAccountRealm($realm);
		return self::selectPublic($realm, true);
	}

	private static function _assertAccountRealm($realm) {
		if(session_status() !== PHP_SESSION_ACTIVE || !self::hasAccountRealm()) return;
		$authenticated = !empty($_SESSION['valid'])
			&& isset($_SESSION['userid'])
			&& isset($_SESSION['username'])
			&& isset($_SESSION['timeout']);
		if($authenticated && self::accountRealm() !== $realm) {
			throw new RuntimeException('La sesión está fijada a otro reino. Cierra sesión antes de operar una cuenta diferente.');
		}
	}

	public static function publicRealm() {
		if(session_status() === PHP_SESSION_ACTIVE && isset($_SESSION[self::SESSION_PUBLIC]) && self::exists($_SESSION[self::SESSION_PUBLIC])) {
			return strtolower($_SESSION[self::SESSION_PUBLIC]);
		}
		if(self::exists(self::$_runtimeRealm)) return self::$_runtimeRealm;
		return self::defaultRealm();
	}

	public static function hasExplicitPublicRealm() {
		return session_status() === PHP_SESSION_ACTIVE
			&& !empty($_SESSION[self::SESSION_PUBLIC_EXPLICIT])
			&& isset($_SESSION[self::SESSION_PUBLIC])
			&& self::exists($_SESSION[self::SESSION_PUBLIC]);
	}

	public static function hasAccountRealm() {
		return session_status() === PHP_SESSION_ACTIVE
			&& isset($_SESSION[self::SESSION_ACCOUNT])
			&& self::exists($_SESSION[self::SESSION_ACCOUNT]);
	}

	public static function accountRealm() {
		if(!self::hasAccountRealm()) return null;
		return strtolower($_SESSION[self::SESSION_ACCOUNT]);
	}

	public static function lockAccountRealm($realm) {
		$realm = self::validate($realm);
		if(session_status() !== PHP_SESSION_ACTIVE) {
			throw new RuntimeException('No se pudo fijar el reino de la sesión.');
		}

		$_SESSION[self::SESSION_ACCOUNT] = $realm;
		$_SESSION[self::SESSION_PUBLIC] = $realm;
		$_SESSION[self::SESSION_PUBLIC_EXPLICIT] = true;
		self::$_runtimeRealm = $realm;
		return $realm;
	}

	public static function clearAccountRealm() {
		if(session_status() === PHP_SESSION_ACTIVE) {
			unset($_SESSION[self::SESSION_ACCOUNT]);
			unset($_SESSION[self::SESSION_OPERATIONAL]);
		}
	}

	public static function active() {
		if(defined('access') && access === 'admincp' && self::hasOperationalRealm()) return self::operationalRealm();
		if(self::hasAccountRealm()) return self::accountRealm();
		if(self::exists(self::$_runtimeRealm)) return self::$_runtimeRealm;
		return self::publicRealm();
	}

	public static function info($realm=null) {
		$config = self::_loadConfig();
		$realm = self::validate($realm === null ? self::active() : $realm);
		return $config['realms'][$realm];
	}

	public static function publicList() {
		$config = self::_loadConfig();
		$result = array();
		foreach($config['realms'] as $id => $realm) {
			$result[$id] = array(
				'id' => $id,
				'label' => isset($realm['label']) ? $realm['label'] : ucfirst($id),
				'short_label' => isset($realm['short_label']) ? $realm['short_label'] : '',
				'description' => isset($realm['description']) ? $realm['description'] : '',
				'accent' => isset($realm['accent']) ? $realm['accent'] : $id,
			);
		}
		return $result;
	}

	public static function label($realm=null) {
		$info = self::info($realm);
		return isset($info['label']) ? $info['label'] : ucfirst(self::active());
	}

	private static function _inherit($realmConfig, $realmKey, $webengineKey, $webengineConfig) {
		if(array_key_exists($realmKey, $realmConfig) && $realmConfig[$realmKey] !== null && $realmConfig[$realmKey] !== '') {
			return $realmConfig[$realmKey];
		}
		return isset($webengineConfig[$webengineKey]) ? $webengineConfig[$webengineKey] : null;
	}

	public static function connection($logicalDatabase, $realm=null) {
		$realm = self::validate($realm === null ? self::active() : $realm);
		$realmConfig = self::info($realm);
		$webengineConfig = webengineConfigs();

		switch($logicalDatabase) {
			case 'MuOnline':
				$database = isset($realmConfig['game_database']) ? $realmConfig['game_database'] : null;
				break;
			case 'Me_MuOnline':
				$database = isset($realmConfig['account_database']) && $realmConfig['account_database'] !== ''
					? $realmConfig['account_database']
					: (isset($realmConfig['game_database']) ? $realmConfig['game_database'] : null);
				break;
			case 'Platform':
				// Migration-friendly mode: until the shared platform database is enabled,
				// platform tables remain in the active realm for compatibility.
				$database = self::platformEnabled()
					? self::platformDatabase()
					: (isset($realmConfig['account_database']) && $realmConfig['account_database'] !== '' ? $realmConfig['account_database'] : $realmConfig['game_database']);
				break;
			default:
				throw new InvalidArgumentException('La conexión lógica solicitada no es válida.');
		}

		if($database === null || $database === '') {
			throw new RuntimeException('El reino seleccionado no tiene una base configurada.');
		}

		return array(
			'realm' => $realm,
			'label' => self::label($realm),
			'host' => self::_inherit($realmConfig, 'host', 'SQL_DB_HOST', $webengineConfig),
			'port' => self::_inherit($realmConfig, 'port', 'SQL_DB_PORT', $webengineConfig),
			'database' => $database,
			'username' => self::_inherit($realmConfig, 'username', 'SQL_DB_USER', $webengineConfig),
			'password' => self::_inherit($realmConfig, 'password', 'SQL_DB_PASS', $webengineConfig),
			'driver' => self::_inherit($realmConfig, 'driver', 'SQL_PDO_DRIVER', $webengineConfig),
		);
	}

	public static function databaseName($logicalDatabase='MuOnline', $realm=null) {
		$connection = self::connection($logicalDatabase, $realm);
		return $connection['database'];
	}

	public static function isRealmCache($fileName) {
		$fileName = basename((string)$fileName);
		if(strpos($fileName, 'rankings_') === 0 && substr($fileName, -6) === '.cache') return true;
		return in_array($fileName, array(
			'server_info.cache',
			'castle_siege.cache',
			'character_country.cache',
			'online_characters.cache',
		), true);
	}

	public static function cachePath($fileName, $realm=null) {
		$fileName = basename((string)$fileName);
		if(!self::isRealmCache($fileName)) return __PATH_CACHE__ . $fileName;
		$realm = self::validate($realm === null ? self::active() : $realm);
		return __PATH_CACHE__ . 'realms/' . $realm . '/' . $fileName;
	}

	public static function realmCacheDirectory($realm=null) {
		$realm = self::validate($realm === null ? self::active() : $realm);
		return __PATH_CACHE__ . 'realms/' . $realm . '/';
	}

	public static function profileCacheDirectory($type, $realm=null) {
		$realm = self::validate($realm === null ? self::active() : $realm);
		$type = $type === 'guilds' ? 'guilds' : 'players';
		return __PATH_CACHE__ . 'realms/' . $realm . '/profiles/' . $type . '/';
	}

	public static function appendToUrl($url, $realm=null) {
		$realm = self::validate($realm === null ? self::active() : $realm);
		$separator = strpos($url, '?') === false ? '?' : '&';
		return $url . $separator . 'realm=' . rawurlencode($realm);
	}

	public static function currentUrlFor($realm) {
		$realm = self::validate($realm);
		$uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/';
		$parts = parse_url($uri);
		$path = isset($parts['path']) ? $parts['path'] : '/';
		$query = array();
		if(isset($parts['query'])) parse_str($parts['query'], $query);
		$query['realm'] = $realm;
		return $path . '?' . http_build_query($query);
	}
}
