<?php

namespace XcVm\Infrastructure;

use XcVm\Core\Config\SettingsManager;
use XcVm\Core\Database\Database;
use XcVm\Core\Http\RequestManager;
use XcVm\Core\Util\TimeUtils;
use XcVm\Domain\Device\EnigmaService;
use XcVm\Domain\Device\MagService;
use XcVm\Domain\Line\PackageService;
use XcVm\Domain\Server\ServerRepository;
use XcVm\Domain\Stream\CategoryService;
use XcVm\Domain\Stream\ConnectionTracker;
use XcVm\Domain\User\UserRepository;
use XcVm\Infrastructure\Redis\RedisManager;

/**
 * ResellerTableRenderer — DataTables handler for reseller panel.
 *
 * All $rType branches are private static handler methods.
 *
 * @package XC_VM_Infrastructure
 * @author  Divarion_D <https://github.com/Divarion-D>
 * @copyright 2025-2026 Vateron Media
 * @link    https://github.com/Vateron-Media/XC_VM
 * @license AGPL-3.0 https://www.gnu.org/licenses/agpl-3.0.html
 */

class ResellerTableRenderer {

	/**
	 * Render a reseller DataTables view for the requested type.
	 *
	 * Dispatches to the matching private handler based on the request type.
	 *
	 * @param array      $rReturn      DataTables request payload.
	 * @param bool       $rIsAPI       Whether the request came via the API.
	 * @param array|null $rUserInfo    Authenticated reseller user (or null).
	 * @param array      $rPermissions Effective permissions.
	 * @param array      $rSettings    Panel settings.
	 * @return void
	 */
	public static function render(array $rReturn, bool $rIsAPI, ?array $rUserInfo, array $rPermissions, array $rSettings): void {
		global $db;

		if (!isset($rUserInfo['reports'])) {
			echo json_encode($rReturn);
			exit();
		}

		$rType = RequestManager::getAll()['id'];
		$rStart = intval(RequestManager::getAll()['start']);
		$rLimit = intval(RequestManager::getAll()['length']);
		if (1000 < $rLimit || $rLimit <= 0) {
			$rLimit = 1000;
		}

		switch ($rType) {
			case 'lines':
				self::handleLines($rReturn, $rIsAPI, $rUserInfo, $rPermissions, $rSettings, $db, $rStart, $rLimit);
				break;
			case 'mags':
				self::handleMags($rReturn, $rIsAPI, $rUserInfo, $rPermissions, $rSettings, $db, $rStart, $rLimit);
				break;
			case 'enigmas':
				self::handleEnigmas($rReturn, $rIsAPI, $rUserInfo, $rPermissions, $rSettings, $db, $rStart, $rLimit);
				break;
			case 'streams':
				self::handleStreams($rReturn, $rIsAPI, $rUserInfo, $rPermissions, $rSettings, $db, $rStart, $rLimit);
				break;
			case 'radios':
				self::handleRadios($rReturn, $rIsAPI, $rUserInfo, $rPermissions, $rSettings, $db, $rStart, $rLimit);
				break;
			case 'movies':
				self::handleMovies($rReturn, $rIsAPI, $rUserInfo, $rPermissions, $rSettings, $db, $rStart, $rLimit);
				break;
			case 'episodes':
				self::handleEpisodes($rReturn, $rIsAPI, $rUserInfo, $rPermissions, $rSettings, $db, $rStart, $rLimit);
				break;
			case 'line_activity':
				self::handleLineActivity($rReturn, $rIsAPI, $rUserInfo, $rPermissions, $rSettings, $db, $rStart, $rLimit);
				break;
			case 'live_connections':
				self::handleLiveConnections($rReturn, $rIsAPI, $rUserInfo, $rPermissions, $rSettings, $db, $rStart, $rLimit);
				break;
			case 'reg_user_logs':
				self::handleRegUserLogs($rReturn, $rIsAPI, $rUserInfo, $rPermissions, $rSettings, $db, $rStart, $rLimit);
				break;
			case 'reg_users':
				self::handleRegUsers($rReturn, $rIsAPI, $rUserInfo, $rPermissions, $rSettings, $db, $rStart, $rLimit);
				break;
		}
	}

	/**
	 * Render the reseller "lines" table.
	 *
	 * @param array  $rReturn      DataTables request/response payload.
	 * @param bool   $rIsAPI       Whether the request came via the API.
	 * @param array  $rUserInfo    Authenticated reseller user.
	 * @param array  $rPermissions Effective permissions.
	 * @param array  $rSettings    Panel settings.
	 * @param \XcVm\Core\Database\DatabaseHandler $db           Database handler.
	 * @param int    $rStart       Pagination offset.
	 * @param int    $rLimit       Page size.
	 * @return void
	 */
	private static function handleLines(array $rReturn, bool $rIsAPI, array $rUserInfo, array $rPermissions, array $rSettings, $db, int $rStart, int $rLimit): void {
		if (!$rPermissions['create_line']) {
			exit();
		}
		$rOrderBy = '';
		$rOrder = array('`lines`.`id`', '`lines`.`username`', '`lines`.`password`', '`users`.`username`', '`lines`.`enabled` - `lines`.`admin_enabled`', '`active_connections` > 0', '`lines`.`is_trial`', '`active_connections`', '`lines`.`max_connections`', '`lines`.`exp_date`', '`last_activity`', false);
		if (isset(RequestManager::getAll()['order']) && 0 < strlen(RequestManager::getAll()['order'][0]['column'])) {
			$rOrderRow = intval(RequestManager::getAll()['order'][0]['column']);
		} else {
			$rOrderRow = 0;
		}
		$rWhere = $rWhereV = array();
		$rWhere[] = '`lines`.`is_mag` = 0 AND `lines`.`is_e2` = 0';
		$rWhere[] = '`lines`.`member_id` IN (' . implode(',', $rUserInfo['reports']) . ')';
		if (0 >= strlen(RequestManager::getAll()['search']['value'])) {
		} else {
			foreach (range(1, 6) as $rInt) {
				$rWhereV[] = '%' . RequestManager::getAll()['search']['value'] . '%';
			}
			$rWhere[] = '(`lines`.`username` LIKE ? OR `lines`.`password` LIKE ? OR `users`.`username` LIKE ? OR FROM_UNIXTIME(`exp_date`) LIKE ? OR `lines`.`max_connections` LIKE ? OR `lines`.`reseller_notes` LIKE ?)';
		}
		if (0 >= strlen(RequestManager::getAll()['filter'])) {
		} else {
			if (RequestManager::getAll()['filter'] == 1) {
				$rWhere[] = '(`lines`.`admin_enabled` = 1 AND `lines`.`enabled` = 1 AND (`lines`.`exp_date` IS NULL OR `lines`.`exp_date` > UNIX_TIMESTAMP()))';
			} else {
				if (RequestManager::getAll()['filter'] == 2) {
					$rWhere[] = '`lines`.`enabled` = 0';
				} else {
					if (RequestManager::getAll()['filter'] == 3) {
						$rWhere[] = '`lines`.`admin_enabled` = 0';
					} else {
						if (RequestManager::getAll()['filter'] == 4) {
							$rWhere[] = '(`lines`.`exp_date` IS NOT NULL AND `lines`.`exp_date` <= UNIX_TIMESTAMP())';
						} else {
							if (RequestManager::getAll()['filter'] != 5) {
							} else {
								$rWhere[] = '`lines`.`is_trial` = 1';
							}
						}
					}
				}
			}
		}
		if (0 >= strlen(RequestManager::getAll()['reseller'])) {
		} else {
			$rWhere[] = '`lines`.`member_id` = ?';
			$rWhereV[] = RequestManager::getAll()['reseller'];
		}
		if (0 < count($rWhere)) {
			$rWhereString = 'WHERE ' . implode(' AND ', $rWhere);
		} else {
			$rWhereString = '';
		}
		$rCountQuery = 'SELECT COUNT(`lines`.`id`) AS `count` FROM `lines` LEFT JOIN `users` ON `users`.`id` = `lines`.`member_id` ' . $rWhereString . ';';
		if (!$rOrder[$rOrderRow]) {
		} else {
			$rOrderDirection = (strtolower(RequestManager::getAll()['order'][0]['dir']) === 'desc' ? 'desc' : 'asc');
			$rOrderBy = 'ORDER BY ' . $rOrder[$rOrderRow] . ' ' . $rOrderDirection;
		}
		$db->query($rCountQuery, ...$rWhereV);
		if ($db->num_rows() == 1) {
			$rReturn['recordsTotal'] = $db->get_row()['count'];
		} else {
			$rReturn['recordsTotal'] = 0;
		}
		$rReturn['recordsFiltered'] = $rReturn['recordsTotal'];
		if (0 >= $rReturn['recordsTotal']) {
		} else {
			$rQuery = 'SELECT `lines`.`id`, `lines`.`member_id`, `lines`.`last_activity`, `lines`.`last_activity_array`, `lines`.`username`, `lines`.`password`, `lines`.`exp_date`, `lines`.`admin_enabled`, `lines`.`is_restreamer`, `lines`.`enabled`, `lines`.`admin_notes`, `lines`.`reseller_notes`, `lines`.`max_connections`, `lines`.`is_trial`, `lines`.`contact`, `lines`.`is_isplock`, (SELECT COUNT(*) AS `active_connections` FROM `lines_live` WHERE `user_id` = `lines`.`id` AND `hls_end` = 0) AS `active_connections` FROM `lines` LEFT JOIN `users` ON `users`.`id` = `lines`.`member_id` ' . $rWhereString . ' ' . $rOrderBy . ' LIMIT ' . $rStart . ', ' . $rLimit . ';';
			$db->query($rQuery, ...$rWhereV);
			if (0 >= $db->num_rows()) {
			} else {
				$rRows = $db->get_rows();
				$rActivityIDs = $rLineInfo = $rLineIDs = array();
				foreach ($rRows as $rRow) {
					$rLineIDs[] = intval($rRow['id']);
					$rLineInfo[intval($rRow['id'])] = array('owner_name' => null, 'stream_display_name' => null, 'stream_id' => null, 'last_active' => null);
					if ($rLastInfo = json_decode($rRow['last_activity_array'], true)) {
						$rLineInfo[intval($rRow['id'])]['stream_id'] = $rLastInfo['stream_id'];
						$rLineInfo[intval($rRow['id'])]['last_active'] = $rLastInfo['date_end'];
					} else {
						if (!$rRow['last_activity']) {
						} else {
							$rActivityIDs[] = intval($rRow['last_activity']);
						}
					}
				}
				if (0 >= count($rLineIDs)) {
				} else {
					$db->query('SELECT `users`.`username`, `lines`.`id` FROM `users` LEFT JOIN `lines` ON `lines`.`member_id` = `users`.`id` WHERE `lines`.`id` IN (' . implode(',', $rLineIDs) . ');');
					foreach ($db->get_rows() as $rRow) {
						$rLineInfo[$rRow['id']]['owner_name'] = $rRow['username'];
					}
					if (SettingsManager::getAll()['redis_handler']) {
						$rConnectionCount = array();
						$rConnectionMap = ConnectionTracker::getUserConnections($rLineIDs, false);
						$rStreamIDs = array();
						foreach ($rConnectionMap as $rUserID => $rConnections) {
							foreach ($rConnections as $rConnection) {
								if (in_array($rConnection['stream_id'], $rStreamIDs)) {
								} else {
									$rStreamIDs[] = intval($rConnection['stream_id']);
								}
							}
						}
						$rStreamMap = array();
						if (0 >= count($rStreamIDs)) {
						} else {
							$db->query('SELECT `id`, `stream_display_name` FROM `streams` WHERE `id` IN (' . implode(',', $rStreamIDs) . ');');
							foreach ($db->get_rows() as $rRow) {
								$rStreamMap[$rRow['id']] = $rRow['stream_display_name'];
							}
						}
						foreach (array_keys($rConnectionMap) as $rUserID) {
							$rDates = array_column($rConnectionMap[$rUserID], 'date_start');
							if (count($rDates) === count($rConnectionMap[$rUserID])) {
								array_multisort($rDates, SORT_DESC, $rConnectionMap[$rUserID]);
							}
							$rLineInfo[$rUserID]['stream_display_name'] = $rStreamMap[$rConnectionMap[$rUserID][0]['stream_id']];
							$rLineInfo[$rUserID]['stream_id'] = $rConnectionMap[$rUserID][0]['stream_id'];
							$rLineInfo[$rUserID]['last_active'] = $rConnectionMap[$rUserID][0]['date_start'];
							$rConnectionCount[$rUserID] = count($rConnectionMap[$rUserID]);
						}
						unset($rConnectionMap);
					} else {
						$db->query('SELECT `lines_live`.`user_id`, `lines_live`.`stream_id`, `lines_live`.`date_start` AS `last_active`, `streams`.`stream_display_name` FROM `lines_live` LEFT JOIN `streams` ON `streams`.`id` = `lines_live`.`stream_id` INNER JOIN (SELECT `user_id`, MAX(`date_start`) AS `ts` FROM `lines_live` GROUP BY `user_id`) `maxt` ON (`lines_live`.`user_id` = `maxt`.`user_id` AND `lines_live`.`date_start` = `maxt`.`ts`) WHERE `lines_live`.`user_id` IN (' . implode(',', $rLineIDs) . ');');
						foreach ($db->get_rows() as $rRow) {
							$rLineInfo[$rRow['user_id']]['stream_display_name'] = $rRow['stream_display_name'];
							$rLineInfo[$rRow['user_id']]['stream_id'] = $rRow['stream_id'];
							$rLineInfo[$rRow['user_id']]['last_active'] = $rRow['last_active'];
						}
					}
				}
				if (0 >= count($rActivityIDs)) {
				} else {
					$db->query('SELECT `user_id`, `stream_id`, `date_end` AS `last_active` FROM `lines_activity` WHERE `activity_id` IN (' . implode(',', $rActivityIDs) . ');');
					foreach ($db->get_rows() as $rRow) {
						if (isset($rLineInfo[$rRow['user_id']]['stream_id'])) {
						} else {
							$rLineInfo[$rRow['user_id']]['stream_id'] = $rRow['stream_id'];
							$rLineInfo[$rRow['user_id']]['last_active'] = $rRow['last_active'];
						}
					}
				}
				foreach ($rRows as $rRow) {
					$rRow = array_merge($rRow, $rLineInfo[$rRow['id']]);
					if (!SettingsManager::getAll()['redis_handler']) {
					} else {
						$rRow['active_connections'] = (isset($rConnectionCount[$rRow['id']]) ? $rConnectionCount[$rRow['id']] : 0);
					}
					if (!$rIsAPI) {
						if (!$rRow['admin_enabled']) {
							$rStatus = '<i class="text-danger fas fa-square tooltip" title="Banned"></i>';
						} else {
							if (!$rRow['enabled']) {
								$rStatus = '<i class="text-secondary fas fa-square tooltip" title="Disabled"></i>';
							} else {
								if ($rRow['exp_date'] && $rRow['exp_date'] < time()) {
									$rStatus = '<i class="text-warning far fa-square tooltip" title="Expired"></i>';
								} else {
									$rStatus = '<i class="text-success fas fa-square tooltip" title="Active"></i>';
								}
							}
						}
						if (0 < $rRow['active_connections']) {
							$rActive = '<i class="text-success fas fa-square"></i>';
						} else {
							$rActive = '<i class="text-secondary far fa-square"></i>';
						}
						if ($rRow['is_trial']) {
							$rTrial = '<i class="text-warning fas fa-square"></i>';
						} else {
							$rTrial = '<i class="text-secondary far fa-square"></i>';
						}
						if ($rRow['exp_date']) {
							if ($rRow['exp_date'] < time()) {
								$rExpDate = '<span class="expired">' . date($rSettings['date_format'], $rRow['exp_date']) . '<br/><small>' . date('H:i:s', $rRow['exp_date']) . '</small></span>';
							} else {
								$rExpDate = date($rSettings['date_format'], $rRow['exp_date']) . "<br/><small class='text-secondary'>" . date('H:i:s', $rRow['exp_date']) . '</small>';
							}
						} else {
							$rExpDate = '&infin;';
						}
						if (0 < $rRow['active_connections']) {
							if ($rPermissions['reseller_client_connection_logs']) {
								$rActiveConnections = "<a href='javascript: void(0);' onClick='viewLiveConnections(" . intval($rRow['id']) . ");'><button type='button' class='btn btn-info btn-xs waves-effect waves-light'>" . $rRow['active_connections'] . '</button></a>';
							} else {
								$rActiveConnections = "<button type='button' class='btn btn-info btn-xs waves-effect waves-light'>" . $rRow['active_connections'] . '</button>';
							}
						} else {
							$rActiveConnections = "<button type='button' class='btn btn-secondary btn-xs waves-effect waves-light'>0</button>";
						}
						if ($rRow['max_connections'] == 0) {
							$rMaxConnections = "<button type='button' class='btn btn-dark text-white btn-xs waves-effect waves-light'>&infin;</button>";
						} else {
							$rMaxConnections = "<button type='button' class='btn btn-secondary btn-xs waves-effect waves-light'>" . $rRow['max_connections'] . '</button>';
						}
						$rButtons = '<div class="btn-group">';
						$rNotes = '';
						if (0 >= strlen($rRow['reseller_notes'])) {
						} else {
							if (strlen($rNotes) == 0) {
							} else {
								$rNotes .= "\n";
							}
							$rNotes .= $rRow['reseller_notes'];
						}
						if (0 < strlen($rNotes)) {
							$rButtons .= '<button type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" title="' . $rNotes . '"><i class="mdi mdi-note"></i></button>';
						} else {
							$rButtons .= '<button type="button" disabled class="btn btn-light waves-effect waves-light btn-xs"><i class="mdi mdi-note"></i></button>';
						}
						$rButtons .= '<a href="line?id=' . $rRow['id'] . '"><button title="Edit" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip"><i class="mdi mdi-pencil-outline"></i></button></a>';
						if (!$rPermissions['allow_download']) {
						} else {
							$rButtons .= "<button type=\"button\" title=\"Download Playlist\" class=\"btn btn-light waves-effect waves-light btn-xs tooltip\" onClick=\"download('" . $rRow['username'] . "', '" . $rRow['password'] . "');\"><i class=\"mdi mdi-download\"></i></button>";
						}
						$rWhatsAppContact = !empty($rRow['contact']) ? addslashes($rRow['contact']) : '';
						$rWhatsAppExp = $rRow['exp_date'] ? $rRow['exp_date'] : 'null';
						$rButtons .= "<button type=\"button\" title=\"WhatsApp Renewal\" class=\"btn btn-success waves-effect waves-light btn-xs tooltip\" onClick=\"openWhatsApp('" . addslashes($rRow['username']) . "', '" . $rWhatsAppContact . "', " . $rWhatsAppExp . ");\"><i class=\"mdi mdi-whatsapp\"></i></button>";
						if (!$rPermissions['reseller_client_connection_logs']) {
						} else {
							if (0 < $rRow['active_connections']) {
								$rButtons .= '<button title="Kill Connections" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="api(' . $rRow['id'] . ", 'kill_line');\"><i class=\"fas fa-hammer\"></i></button>";
							} else {
								$rButtons .= '<button disabled type="button" class="btn btn-light waves-effect waves-light btn-xs"><i class="fas fa-hammer"></i></button>';
							}
						}
						if (!$rRow['is_isplock']) {
						} else {
							$rButtons .= '<button title="Reset ISP Lock" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="api(' . $rRow['id'] . ", 'reset_isp');\"><i class=\"mdi mdi-lock-reset\"></i></button>";
						}
						if ($rRow['enabled']) {
							$rButtons .= '<button title="Disable" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="api(' . $rRow['id'] . ", 'disable');\"><i class=\"mdi mdi-lock\"></i></button>";
						} else {
							$rButtons .= '<button title="Enable" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="api(' . $rRow['id'] . ", 'enable');\"><i class=\"mdi mdi-lock\"></i></button>";
						}
						$rButtons .= '<button title="Delete" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="api(' . $rRow['id'] . ", 'delete');\"><i class=\"mdi mdi-close\"></i></button>";
						$rButtons .= '</div>';
						if ($rRow['active_connections'] && $rRow['last_active']) {
							$rLastActive = "<a href='stream_view?id=" . $rRow['stream_id'] . "'>" . $rRow['stream_display_name'] . "</a><br/><small class='text-secondary'>Online: " . TimeUtils::secondsToTime(time() - $rRow['last_active']) . '</small>';
						} else {
							if ($rRow['last_active']) {
								$rLastActive = date($rSettings['date_format'], $rRow['last_active']) . "<br/><small class='text-secondary'>" . date('H:i:s', $rRow['last_active']) . '</small>';
							} else {
								$rLastActive = 'Never';
							}
						}
						if (in_array($rRow['member_id'], array_merge($rPermissions['direct_reports'], array($rUserInfo['id'])))) {
							$rOwner = "<a href='user?id=" . intval($rRow['member_id']) . "'>" . $rRow['owner_name'] . '</a>';
						} else {
							$rOwner = "<a href='user?id=" . intval($rRow['member_id']) . "'>" . $rRow['owner_name'] . "<br/><small class='text-pink'>(indirect)</small></a>";
						}
						$rReturn['data'][] = array("<a href='line?id=" . $rRow['id'] . "'>" . $rRow['id'] . '</a>', "<a href='line?id=" . $rRow['id'] . "'>" . $rRow['username'] . '</a>', $rRow['password'], $rOwner, $rStatus, $rActive, $rTrial, $rActiveConnections, $rMaxConnections, $rExpDate, $rLastActive, $rButtons);
					} else {
						$rReturn['data'][] = self::filterRow($rRow, RequestManager::getAll()['show_columns'], RequestManager::getAll()['hide_columns']);
					}
				}
			}
		}
		echo json_encode($rReturn);
		exit();
	}

	/**
	 * Render the reseller "MAG devices" table.
	 *
	 * @param array  $rReturn      DataTables request/response payload.
	 * @param bool   $rIsAPI       Whether the request came via the API.
	 * @param array  $rUserInfo    Authenticated reseller user.
	 * @param array  $rPermissions Effective permissions.
	 * @param array  $rSettings    Panel settings.
	 * @param \XcVm\Core\Database\DatabaseHandler $db           Database handler.
	 * @param int    $rStart       Pagination offset.
	 * @param int    $rLimit       Page size.
	 * @return void
	 */
	private static function handleMags(array $rReturn, bool $rIsAPI, array $rUserInfo, array $rPermissions, array $rSettings, $db, int $rStart, int $rLimit): void {
		if (!$rPermissions['create_mag']) {
			exit();
		}
		$rOrderBy = '';
		$rOrder = array('`lines`.`id`', '`lines`.`username`', '`mag_devices`.`mac`', '`mag_devices`.`stb_type`', '`users`.`username`', '`lines`.`enabled`', '`active_connections`', '`lines`.`is_trial`', '`lines`.`exp_date`', false);
		if (isset(RequestManager::getAll()['order']) && 0 < strlen(RequestManager::getAll()['order'][0]['column'])) {
			$rOrderRow = intval(RequestManager::getAll()['order'][0]['column']);
		} else {
			$rOrderRow = 0;
		}
		$rWhere = $rWhereV = array();
		$rWhere[] = '`lines`.`member_id` IN (' . implode(',', $rUserInfo['reports']) . ')';
		if (0 >= strlen(RequestManager::getAll()['search']['value'])) {
		} else {
			foreach (range(1, 6) as $rInt) {
				$rWhereV[] = '%' . RequestManager::getAll()['search']['value'] . '%';
			}
			$rWhere[] = '(`lines`.`username` LIKE ? OR `mag_devices`.`mac` LIKE ? OR `mag_devices`.`stb_type` LIKE ? OR `users`.`username` LIKE ? OR FROM_UNIXTIME(`exp_date`) LIKE ? OR `lines`.`reseller_notes` LIKE ?)';
		}
		if (0 >= strlen(RequestManager::getAll()['filter'])) {
		} else {
			if (RequestManager::getAll()['filter'] == 1) {
				$rWhere[] = '(`lines`.`admin_enabled` = 1 AND `lines`.`enabled` = 1 AND (`lines`.`exp_date` IS NULL OR `lines`.`exp_date` > UNIX_TIMESTAMP()))';
			} else {
				if (RequestManager::getAll()['filter'] == 2) {
					$rWhere[] = '`lines`.`enabled` = 0';
				} else {
					if (RequestManager::getAll()['filter'] == 3) {
						$rWhere[] = '(`lines`.`exp_date` IS NOT NULL AND `lines`.`exp_date` <= UNIX_TIMESTAMP())';
					} else {
						if (RequestManager::getAll()['filter'] != 4) {
						} else {
							$rWhere[] = '`lines`.`is_trial` = 1';
						}
					}
				}
			}
		}
		if (0 >= strlen(RequestManager::getAll()['reseller'])) {
		} else {
			$rWhere[] = '`lines`.`member_id` = ?';
			$rWhereV[] = RequestManager::getAll()['reseller'];
		}
		if (0 < count($rWhere)) {
			$rWhereString = 'WHERE ' . implode(' AND ', $rWhere);
		} else {
			$rWhereString = '';
		}
		if (!$rOrder[$rOrderRow]) {
		} else {
			$rOrderDirection = (strtolower(RequestManager::getAll()['order'][0]['dir']) === 'desc' ? 'desc' : 'asc');
			$rOrderBy = 'ORDER BY ' . $rOrder[$rOrderRow] . ' ' . $rOrderDirection;
		}
		$rCountQuery = 'SELECT COUNT(`lines`.`id`) AS `count` FROM `lines` LEFT JOIN `users` ON `users`.`id` = `lines`.`member_id` INNER JOIN `mag_devices` ON `mag_devices`.`user_id` = `lines`.`id` ' . $rWhereString . ';';
		$db->query($rCountQuery, ...$rWhereV);
		if ($db->num_rows() == 1) {
			$rReturn['recordsTotal'] = $db->get_row()['count'];
		} else {
			$rReturn['recordsTotal'] = 0;
		}
		$rReturn['recordsFiltered'] = $rReturn['recordsTotal'];
		if (0 >= $rReturn['recordsTotal']) {
		} else {
			$rQuery = 'SELECT `lines`.`id`, `lines`.`username`, `lines`.`member_id`, `lines`.`is_isplock`, `mag_devices`.`mac`, `mag_devices`.`stb_type`, `mag_devices`.`mag_id`, `lines`.`exp_date`, `lines`.`admin_enabled`, `lines`.`enabled`, `lines`.`reseller_notes`, `lines`.`max_connections`,  `lines`.`is_trial`, `users`.`username` AS `owner_name`, (SELECT count(*) FROM `lines_live` WHERE `lines`.`id` = `lines_live`.`user_id` AND `hls_end` = 0) AS `active_connections` FROM `lines` LEFT JOIN `users` ON `users`.`id` = `lines`.`member_id` INNER JOIN `mag_devices` ON `mag_devices`.`user_id` = `lines`.`id` ' . $rWhereString . ' ' . $rOrderBy . ' LIMIT ' . $rStart . ', ' . $rLimit . ';';
			$db->query($rQuery, ...$rWhereV);
			if (0 >= $db->num_rows()) {
			} else {
				$rRows = $db->get_rows();
				$rLineIDs = array();
				foreach ($rRows as $rRow) {
					if (!$rRow['id']) {
					} else {
						$rLineIDs[] = intval($rRow['id']);
					}
				}
				if (0 >= count($rLineIDs)) {
				} else {
					if (!SettingsManager::getAll()['redis_handler']) {
					} else {
						$rConnectionCount = array();
						$rConnectionMap = ConnectionTracker::getUserConnections($rLineIDs, false);
						foreach (array_keys($rConnectionMap) as $rUserID) {
							$rConnectionCount[$rUserID] = count($rConnectionMap[$rUserID]);
						}
						unset($rConnectionMap);
					}
				}
				foreach ($rRows as $rRow) {
					if (!SettingsManager::getAll()['redis_handler']) {
					} else {
						$rRow['active_connections'] = (isset($rConnectionCount[$rRow['id']]) ? $rConnectionCount[$rRow['id']] : 0);
					}
					if (!$rIsAPI) {
						if (!$rRow['admin_enabled']) {
							$rStatus = '<i class="text-danger fas fa-square"></i>';
						} else {
							if (!$rRow['enabled']) {
								$rStatus = '<i class="text-secondary fas fa-square"></i>';
							} else {
								if ($rRow['exp_date'] && $rRow['exp_date'] < time()) {
									$rStatus = '<i class="text-warning far fa-square"></i>';
								} else {
									$rStatus = '<i class="text-success fas fa-square"></i>';
								}
							}
						}
						if (0 < $rRow['active_connections']) {
							$rActive = '<i class="text-success fas fa-square"></i>';
						} else {
							$rActive = '<i class="text-warning far fa-square"></i>';
						}
						if ($rRow['is_trial']) {
							$rTrial = '<i class="text-warning fas fa-square"></i>';
						} else {
							$rTrial = '<i class="text-secondary far fa-square"></i>';
						}
						if ($rRow['exp_date']) {
							if ($rRow['exp_date'] < time()) {
								$rExpDate = '<span class="expired">' . date($rSettings['date_format'], $rRow['exp_date']) . '<br/><small>' . date('H:i:s', $rRow['exp_date']) . '</small></span>';
							} else {
								$rExpDate = date($rSettings['date_format'], $rRow['exp_date']) . "<br/><small class='text-secondary'>" . date('H:i:s', $rRow['exp_date']) . '</small>';
							}
						} else {
							$rExpDate = '&infin;';
						}
						$rButtons = '<div class="btn-group">';
						$rNotes = '';
						if (0 >= strlen($rRow['reseller_notes'])) {
						} else {
							if (strlen($rNotes) == 0) {
							} else {
								$rNotes .= "\n";
							}
							$rNotes .= $rRow['reseller_notes'];
						}
						if (0 < strlen($rNotes)) {
							$rButtons .= '<button type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" title="' . $rNotes . '"><i class="mdi mdi-note"></i></button>';
						} else {
							$rButtons .= '<button type="button" disabled class="btn btn-light waves-effect waves-light btn-xs"><i class="mdi mdi-note"></i></button>';
						}
						$rButtons .= '<button title="MAG Event" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="message(' . $rRow['mag_id'] . ", '" . $rRow['mac'] . "');\"><i class=\"mdi mdi-message-alert\"></i></button>";
						$rButtons .= '<a href="mag?id=' . $rRow['mag_id'] . '"><button title="Edit" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip"><i class="mdi mdi-pencil-outline"></i></button></a>';
						if (!$rRow['is_isplock']) {
						} else {
							$rButtons .= '<button title="Reset ISP Lock" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="api(' . $rRow['mag_id'] . ", 'reset_isp');\"><i class=\"mdi mdi-lock-reset\"></i></button>";
						}
						if (!$rPermissions['create_line']) {
						} else {
							$rButtons .= '<button title="Convert to User Line" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="api(' . $rRow['mag_id'] . ", 'convert');\"><i class=\"fas fa-retweet\"></i></button>";
						}
						if (!$rPermissions['reseller_client_connection_logs']) {
						} else {
							if (0 < $rRow['active_connections']) {
								$rButtons .= '<button title="Kill Connections" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="api(' . $rRow['mag_id'] . ", 'kill_line');\"><i class=\"fas fa-hammer\"></i></button>";
							} else {
								$rButtons .= '<button disabled type="button" class="btn btn-light waves-effect waves-light btn-xs"><i class="fas fa-hammer"></i></button>';
							}
						}
						if ($rRow['enabled'] == 1) {
							$rButtons .= '<button title="Disable" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="api(' . $rRow['mag_id'] . ", 'disable');\"><i class=\"mdi mdi-lock\"></i></button>";
						} else {
							$rButtons .= '<button title="Enable" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="api(' . $rRow['mag_id'] . ", 'enable');\"><i class=\"mdi mdi-lock\"></i></button>";
						}
						$rButtons .= '<button title="Delete" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="api(' . $rRow['mag_id'] . ", 'delete');\"><i class=\"mdi mdi-close\"></i></button>";
						$rButtons .= '</div>';
						if (in_array($rRow['member_id'], array_merge($rPermissions['direct_reports'], array($rUserInfo['id'])))) {
							$rOwner = "<a href='user?id=" . intval($rRow['member_id']) . "'>" . $rRow['owner_name'] . '</a>';
						} else {
							$rOwner = "<a href='user?id=" . intval($rRow['member_id']) . "'>" . $rRow['owner_name'] . "<br/><small class='text-pink'>(indirect)</small></a>";
						}
						$rReturn['data'][] = array("<a href='mag?id=" . $rRow['mag_id'] . "'>" . $rRow['mag_id'] . '</a>', $rRow['username'], "<a href='mag?id=" . $rRow['mag_id'] . "'>" . $rRow['mac'] . '</a>', $rRow['stb_type'], $rOwner, $rStatus, $rActive, $rTrial, $rExpDate, $rButtons);
					} else {
						$rReturn['data'][] = self::filterRow($rRow, RequestManager::getAll()['show_columns'], RequestManager::getAll()['hide_columns']);
					}
				}
			}
		}
		echo json_encode($rReturn);
		exit();
	}

	/**
	 * Render the reseller "Enigma2 devices" table.
	 *
	 * @param array  $rReturn      DataTables request/response payload.
	 * @param bool   $rIsAPI       Whether the request came via the API.
	 * @param array  $rUserInfo    Authenticated reseller user.
	 * @param array  $rPermissions Effective permissions.
	 * @param array  $rSettings    Panel settings.
	 * @param \XcVm\Core\Database\DatabaseHandler $db           Database handler.
	 * @param int    $rStart       Pagination offset.
	 * @param int    $rLimit       Page size.
	 * @return void
	 */
	private static function handleEnigmas(array $rReturn, bool $rIsAPI, array $rUserInfo, array $rPermissions, array $rSettings, $db, int $rStart, int $rLimit): void {
		if (!$rPermissions['create_enigma']) {
			exit();
		}
		$rOrderBy = '';
		$rOrder = array('`lines`.`id`', '`lines`.`username`', '`enigma2_devices`.`mac`', '`enigma2_devices`.`public_ip`', '`users`.`username`', '`lines`.`enabled`', '`active_connections`', '`lines`.`is_trial`', '`lines`.`exp_date`', false);
		if (isset(RequestManager::getAll()['order']) && 0 < strlen(RequestManager::getAll()['order'][0]['column'])) {
			$rOrderRow = intval(RequestManager::getAll()['order'][0]['column']);
		} else {
			$rOrderRow = 0;
		}
		$rWhere = $rWhereV = array();
		$rWhere[] = '`lines`.`member_id` IN (' . implode(',', $rUserInfo['reports']) . ')';
		if (0 >= strlen(RequestManager::getAll()['search']['value'])) {
		} else {
			foreach (range(1, 6) as $rInt) {
				$rWhereV[] = '%' . RequestManager::getAll()['search']['value'] . '%';
			}
			$rWhere[] = '(`lines`.`username` LIKE ? OR `enigma2_devices`.`mac` LIKE ? OR `enigma2_devices`.`public_ip` LIKE ? OR `users`.`username` LIKE ? OR FROM_UNIXTIME(`exp_date`) LIKE ? OR `lines`.`reseller_notes` LIKE ?)';
		}
		if (0 >= strlen(RequestManager::getAll()['filter'])) {
		} else {
			if (RequestManager::getAll()['filter'] == 1) {
				$rWhere[] = '(`lines`.`admin_enabled` = 1 AND `lines`.`enabled` = 1 AND (`lines`.`exp_date` IS NULL OR `lines`.`exp_date` > UNIX_TIMESTAMP()))';
			} else {
				if (RequestManager::getAll()['filter'] == 2) {
					$rWhere[] = '`lines`.`enabled` = 0';
				} else {
					if (RequestManager::getAll()['filter'] == 3) {
						$rWhere[] = '(`lines`.`exp_date` IS NOT NULL AND `lines`.`exp_date` <= UNIX_TIMESTAMP())';
					} else {
						if (RequestManager::getAll()['filter'] != 4) {
						} else {
							$rWhere[] = '`lines`.`is_trial` = 1';
						}
					}
				}
			}
		}
		if (0 >= strlen(RequestManager::getAll()['reseller'])) {
		} else {
			$rWhere[] = '`lines`.`member_id` = ?';
			$rWhereV[] = RequestManager::getAll()['reseller'];
		}
		if (0 < count($rWhere)) {
			$rWhereString = 'WHERE ' . implode(' AND ', $rWhere);
		} else {
			$rWhereString = '';
		}
		if (!$rOrder[$rOrderRow]) {
		} else {
			$rOrderDirection = (strtolower(RequestManager::getAll()['order'][0]['dir']) === 'desc' ? 'desc' : 'asc');
			$rOrderBy = 'ORDER BY ' . $rOrder[$rOrderRow] . ' ' . $rOrderDirection;
		}
		$rCountQuery = 'SELECT COUNT(`lines`.`id`) AS `count` FROM `lines` LEFT JOIN `users` ON `users`.`id` = `lines`.`member_id` INNER JOIN `enigma2_devices` ON `enigma2_devices`.`user_id` = `lines`.`id` ' . $rWhereString . ';';
		$db->query($rCountQuery, ...$rWhereV);
		if ($db->num_rows() == 1) {
			$rReturn['recordsTotal'] = $db->get_row()['count'];
		} else {
			$rReturn['recordsTotal'] = 0;
		}
		$rReturn['recordsFiltered'] = $rReturn['recordsTotal'];
		if (0 >= $rReturn['recordsTotal']) {
		} else {
			$rQuery = 'SELECT `lines`.`id`, `lines`.`username`, `lines`.`member_id`, `lines`.`is_isplock`, `enigma2_devices`.`mac`, `enigma2_devices`.`public_ip`, `enigma2_devices`.`device_id`, `lines`.`exp_date`, `lines`.`admin_enabled`, `lines`.`enabled`, `lines`.`reseller_notes`, `lines`.`max_connections`,  `lines`.`is_trial`, `users`.`username` AS `owner_name`, (SELECT count(*) FROM `lines_live` WHERE `lines`.`id` = `lines_live`.`user_id` AND `hls_end` = 0) AS `active_connections` FROM `lines` LEFT JOIN `users` ON `users`.`id` = `lines`.`member_id` INNER JOIN `enigma2_devices` ON `enigma2_devices`.`user_id` = `lines`.`id` ' . $rWhereString . ' ' . $rOrderBy . ' LIMIT ' . $rStart . ', ' . $rLimit . ';';
			$db->query($rQuery, ...$rWhereV);
			if (0 >= $db->num_rows()) {
			} else {
				$rRows = $db->get_rows();
				$rLineIDs = array();
				foreach ($rRows as $rRow) {
					if (!$rRow['id']) {
					} else {
						$rLineIDs[] = intval($rRow['id']);
					}
				}
				if (0 >= count($rLineIDs)) {
				} else {
					if (!SettingsManager::getAll()['redis_handler']) {
					} else {
						$rConnectionCount = array();
						$rConnectionMap = ConnectionTracker::getUserConnections($rLineIDs, false);
						foreach (array_keys($rConnectionMap) as $rUserID) {
							$rConnectionCount[$rUserID] = count($rConnectionMap[$rUserID]);
						}
						unset($rConnectionMap);
					}
				}
				foreach ($rRows as $rRow) {
					if (!SettingsManager::getAll()['redis_handler']) {
					} else {
						$rRow['active_connections'] = (isset($rConnectionCount[$rRow['id']]) ? $rConnectionCount[$rRow['id']] : 0);
					}
					if (!$rIsAPI) {
						if (!$rRow['admin_enabled']) {
							$rStatus = '<i class="text-danger fas fa-square"></i>';
						} else {
							if (!$rRow['enabled']) {
								$rStatus = '<i class="text-secondary fas fa-square"></i>';
							} else {
								if ($rRow['exp_date'] && $rRow['exp_date'] < time()) {
									$rStatus = '<i class="text-warning far fa-square"></i>';
								} else {
									$rStatus = '<i class="text-success fas fa-square"></i>';
								}
							}
						}
						if (0 < $rRow['active_connections']) {
							$rActive = '<i class="text-success fas fa-square"></i>';
						} else {
							$rActive = '<i class="text-warning far fa-square"></i>';
						}
						if ($rRow['is_trial']) {
							$rTrial = '<i class="text-warning fas fa-square"></i>';
						} else {
							$rTrial = '<i class="text-secondary far fa-square"></i>';
						}
						if ($rRow['exp_date']) {
							if ($rRow['exp_date'] < time()) {
								$rExpDate = '<span class="expired">' . date($rSettings['date_format'], $rRow['exp_date']) . '<br/><small>' . date('H:i:s', $rRow['exp_date']) . '</small></span>';
							} else {
								$rExpDate = date($rSettings['date_format'], $rRow['exp_date']) . "<br/><small class='text-secondary'>" . date('H:i:s', $rRow['exp_date']) . '</small>';
							}
						} else {
							$rExpDate = '&infin;';
						}
						$rButtons = '<div class="btn-group">';
						$rNotes = '';
						if (0 >= strlen($rRow['reseller_notes'])) {
						} else {
							if (strlen($rNotes) == 0) {
							} else {
								$rNotes .= "\n";
							}
							$rNotes .= $rRow['reseller_notes'];
						}
						if (0 < strlen($rNotes)) {
							$rButtons .= '<button type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" title="' . $rNotes . '"><i class="mdi mdi-note"></i></button>';
						} else {
							$rButtons .= '<button type="button" disabled class="btn btn-light waves-effect waves-light btn-xs"><i class="mdi mdi-note"></i></button>';
						}
						$rButtons .= '<a href="enigma?id=' . $rRow['device_id'] . '"><button title="Edit" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip"><i class="mdi mdi-pencil-outline"></i></button></a>';
						if (!$rRow['is_isplock']) {
						} else {
							$rButtons .= '<button title="Reset ISP Lock" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="api(' . $rRow['device_id'] . ", 'reset_isp');\"><i class=\"mdi mdi-lock-reset\"></i></button>";
						}
						if (!$rPermissions['create_line']) {
						} else {
							$rButtons .= '<button title="Convert to User Line" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="api(' . $rRow['device_id'] . ", 'convert');\"><i class=\"fas fa-retweet\"></i></button>";
						}
						if (!$rPermissions['reseller_client_connection_logs']) {
						} else {
							if (0 < $rRow['active_connections']) {
								$rButtons .= '<button title="Kill Connections" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="api(' . $rRow['device_id'] . ", 'kill_line');\"><i class=\"fas fa-hammer\"></i></button>";
							} else {
								$rButtons .= '<button disabled type="button" class="btn btn-light waves-effect waves-light btn-xs"><i class="fas fa-hammer"></i></button>';
							}
						}
						if ($rRow['enabled'] == 1) {
							$rButtons .= '<button title="Disable" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="api(' . $rRow['device_id'] . ", 'disable');\"><i class=\"mdi mdi-lock\"></i></button>";
						} else {
							$rButtons .= '<button title="Enable" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="api(' . $rRow['device_id'] . ", 'enable');\"><i class=\"mdi mdi-lock\"></i></button>";
						}
						$rButtons .= '<button title="Delete" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="api(' . $rRow['device_id'] . ", 'delete');\"><i class=\"mdi mdi-close\"></i></button>";
						$rButtons .= '</div>';
						if (in_array($rRow['member_id'], array_merge($rPermissions['direct_reports'], array($rUserInfo['id'])))) {
							$rOwner = "<a href='user?id=" . intval($rRow['member_id']) . "'>" . $rRow['owner_name'] . '</a>';
						} else {
							$rOwner = "<a href='user?id=" . intval($rRow['member_id']) . "'>" . $rRow['owner_name'] . "<br/><small class='text-pink'>(indirect)</small></a>";
						}
						$rReturn['data'][] = array("<a href='enigma?id=" . $rRow['device_id'] . "'>" . $rRow['device_id'] . '</a>', $rRow['username'], "<a href='enigma?id=" . $rRow['device_id'] . "'>" . $rRow['mac'] . '</a>', "<a onClick=\"whois('" . $rRow['public_ip'] . "');\" href='javascript: void(0);'>" . $rRow['public_ip'] . '</a>', $rOwner, $rStatus, $rActive, $rTrial, $rExpDate, $rButtons);
					} else {
						$rReturn['data'][] = self::filterRow($rRow, RequestManager::getAll()['show_columns'], RequestManager::getAll()['hide_columns']);
					}
				}
			}
		}
		echo json_encode($rReturn);
		exit();
	}

	/**
	 * Render the reseller "streams" table.
	 *
	 * @param array  $rReturn      DataTables request/response payload.
	 * @param bool   $rIsAPI       Whether the request came via the API.
	 * @param array  $rUserInfo    Authenticated reseller user.
	 * @param array  $rPermissions Effective permissions.
	 * @param array  $rSettings    Panel settings.
	 * @param \XcVm\Core\Database\DatabaseHandler $db           Database handler.
	 * @param int    $rStart       Pagination offset.
	 * @param int    $rLimit       Page size.
	 * @return void
	 */
	private static function handleStreams(array $rReturn, bool $rIsAPI, array $rUserInfo, array $rPermissions, array $rSettings, $db, int $rStart, int $rLimit): void {
		if (!$rPermissions['can_view_vod']) {
			exit();
		}
		$rCategories = CategoryService::getAllByType('live');
		$rOrderBy = '';
		$rOrder = array('`id`', false, '`stream_display_name`', '`category_id`', '`clients`', false);
		if (isset(RequestManager::getAll()['order']) && 0 < strlen(RequestManager::getAll()['order'][0]['column'])) {
			$rOrderRow = intval(RequestManager::getAll()['order'][0]['column']);
		} else {
			$rOrderRow = 0;
		}
		$rCreated = isset(RequestManager::getAll()['created']);
		$rWhere = $rWhereV = array();
		if (0 < count($rPermissions['stream_ids'])) {
			$rWhere[] = '`streams`.`id` IN (' . implode(',', array_map('intval', $rPermissions['stream_ids'])) . ')';
			if ($rCreated) {
				$rWhere[] = '`type` = 3';
			} else {
				$rWhere[] = '`type` = 1';
			}
			if (0 >= strlen(RequestManager::getAll()['search']['value'])) {
			} else {
				foreach (range(1, 2) as $rInt) {
					$rWhereV[] = '%' . RequestManager::getAll()['search']['value'] . '%';
				}
				$rWhere[] = '(`id` LIKE ? OR `stream_display_name` LIKE ?)';
			}
			if (0 >= strlen(RequestManager::getAll()['category'])) {
			} else {
				$rWhere[] = "JSON_CONTAINS(`category_id`, ?, '\$')";
				$rWhereV[] = RequestManager::getAll()['category'];
			}
			if (!$rOrder[$rOrderRow]) {
			} else {
				$rOrderDirection = (strtolower(RequestManager::getAll()['order'][0]['dir']) === 'desc' ? 'desc' : 'asc');
				$rOrderBy = 'ORDER BY ' . $rOrder[$rOrderRow] . ' ' . $rOrderDirection;
			}
			if (0 < count($rWhere)) {
				$rWhereString = 'WHERE ' . implode(' AND ', $rWhere);
			} else {
				$rWhereString = '';
			}
			$rCountQuery = 'SELECT COUNT(`streams`.`id`) AS `count` FROM `streams` ' . $rWhereString . ';';
			$db->query($rCountQuery, ...$rWhereV);
			if ($db->num_rows() == 1) {
				$rReturn['recordsTotal'] = $db->get_row()['count'];
			} else {
				$rReturn['recordsTotal'] = 0;
			}
			$rReturn['recordsFiltered'] = $rReturn['recordsTotal'];
			if (0 >= $rReturn['recordsTotal']) {
			} else {
				$rQuery = 'SELECT `id`, `stream_icon`, `stream_display_name`, `tv_archive_duration`, `tv_archive_server_id`, `category_id`, (SELECT COUNT(*) FROM `lines_live` LEFT JOIN `lines` ON `lines`.`id` = `lines_live`.`user_id` WHERE `lines_live`.`stream_id` = `streams`.`id` AND `hls_end` = 0 AND `lines`.`member_id` IN (' . implode(',', $rUserInfo['reports']) . ')) AS `clients` FROM `streams` ' . $rWhereString . ' ' . $rOrderBy . ' LIMIT ' . $rStart . ', ' . $rLimit . ';';
				$db->query($rQuery, ...$rWhereV);
				if ($db->num_rows() > 0) {
					$rRows = $db->get_rows();
					if (SettingsManager::getAll()['redis_handler']) {
						$rConnectionCount = $rReports = array();
						$db->query('SELECT `id` FROM `lines` WHERE `member_id` IN (' . implode(',', $rUserInfo['reports']) . ');');
						foreach ($db->get_rows() as $rRow) {
							$rReports[] = $rRow['id'];
						}
						if (0 >= count($rReports)) {
						} else {
							foreach (ConnectionTracker::getUserConnections($rReports, false) as $rUserID => $rConnections) {
								foreach ($rConnections as $rConnection) {
									$rConnectionCount[$rConnection['stream_id']]++;
								}
							}
						}
					}
					foreach ($rRows as $rRow) {
						if (!SettingsManager::getAll()['redis_handler']) {
						} else {
							$rRow['clients'] = ($rConnectionCount[$rRow['id']] ?: 0);
						}
						if (!$rIsAPI) {
							$rCategoryIDs = json_decode($rRow['category_id'], true);
							if (0 < strlen(RequestManager::getAll()['category'])) {
								$rCategory = ($rCategories[intval(RequestManager::getAll()['category'])]['category_name'] ?: 'No Category');
							} else {
								$rCategory = ($rCategories[$rCategoryIDs[0]]['category_name'] ?: 'No Category');
							}
							if (1 >= count($rCategoryIDs)) {
							} else {
								$rCategory .= ' (+' . (count($rCategoryIDs) - 1) . ' others)';
							}
							if (!(0 < $rRow['tv_archive_duration'] && 0 < $rRow['tv_archive_server_id'])) {
							} else {
								$rRow['stream_display_name'] .= " <i class='text-danger mdi mdi-record'></i>";
							}
							if (0 < $rRow['clients']) {
								if ($rPermissions['reseller_client_connection_logs']) {
									$rButtons = '<button title="Kill Connections" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="api(' . $rRow['id'] . ", 'purge');\"><i class=\"mdi mdi-hammer\"></i></button>";
									$rClients = "<a href='javascript: void(0);' onClick='viewLiveConnections(" . intval($rRow['id']) . ");'><button type='button' class='btn btn-info btn-xs waves-effect waves-light'>" . $rRow['clients'] . '</button></a>';
								} else {
									$rButtons = '<button type="button" disabled class="btn btn-light waves-effect waves-light btn-xs"><i class="mdi mdi-hammer"></i></button>';
									$rClients = "<button type='button' class='btn btn-info btn-xs waves-effect waves-light'>" . $rRow['clients'] . '</button>';
								}
							} else {
								$rButtons = '<button type="button" disabled class="btn btn-light waves-effect waves-light btn-xs"><i class="mdi mdi-hammer"></i></button>';
								$rClients = "<button type='button' class='btn btn-secondary btn-xs waves-effect waves-light'>0</button>";
							}
							if (0 < strlen($rRow['stream_icon'])) {
								$rIcon = "<a href='javascript: void(0);' onClick='openImage(this);' data-src='resize?maxw=512&maxh=512&url=" . $rRow['stream_icon'] . "'><img loading='lazy' src='resize?maxw=96&maxh=32&url=" . $rRow['stream_icon'] . "' /></a>";
							} else {
								$rIcon = '';
							}
							$rReturn['data'][] = array($rRow['id'], $rIcon, $rRow['stream_display_name'], $rCategory, $rClients, $rButtons);
						} else {
							$rReturn['data'][] = self::filterRow($rRow, RequestManager::getAll()['show_columns'], RequestManager::getAll()['hide_columns']);
						}
					}
				}
			}
			echo json_encode($rReturn);
			exit();
		}
		echo json_encode($rReturn);
		exit();
	}

	/**
	 * Render the reseller "radios" table.
	 *
	 * @param array  $rReturn      DataTables request/response payload.
	 * @param bool   $rIsAPI       Whether the request came via the API.
	 * @param array  $rUserInfo    Authenticated reseller user.
	 * @param array  $rPermissions Effective permissions.
	 * @param array  $rSettings    Panel settings.
	 * @param \XcVm\Core\Database\DatabaseHandler $db           Database handler.
	 * @param int    $rStart       Pagination offset.
	 * @param int    $rLimit       Page size.
	 * @return void
	 */
	private static function handleRadios(array $rReturn, bool $rIsAPI, array $rUserInfo, array $rPermissions, array $rSettings, $db, int $rStart, int $rLimit): void {
		if (!$rPermissions['can_view_vod']) {
			exit();
		}
		$rCategories = CategoryService::getAllByType('radio');
		$rOrderBy = '';
		$rOrder = array('`id`', false, '`stream_display_name`', '`category_id`', '`clients`', false);
		if (isset(RequestManager::getAll()['order']) && 0 < strlen(RequestManager::getAll()['order'][0]['column'])) {
			$rOrderRow = intval(RequestManager::getAll()['order'][0]['column']);
		} else {
			$rOrderRow = 0;
		}
		$rCreated = isset(RequestManager::getAll()['created']);
		$rWhere = $rWhereV = array();
		if (0 < count($rPermissions['stream_ids'])) {
			$rWhere[] = '`streams`.`id` IN (' . implode(',', array_map('intval', $rPermissions['stream_ids'])) . ')';
			$rWhere[] = '`type` = 4';
			if (0 >= strlen(RequestManager::getAll()['search']['value'])) {
			} else {
				foreach (range(1, 2) as $rInt) {
					$rWhereV[] = '%' . RequestManager::getAll()['search']['value'] . '%';
				}
				$rWhere[] = '(`id` LIKE ? OR `stream_display_name` LIKE ?)';
			}
			if (0 >= strlen(RequestManager::getAll()['category'])) {
			} else {
				$rWhere[] = "JSON_CONTAINS(`category_id`, ?, '\$')";
				$rWhereV[] = RequestManager::getAll()['category'];
			}
			if (!$rOrder[$rOrderRow]) {
			} else {
				$rOrderDirection = (strtolower(RequestManager::getAll()['order'][0]['dir']) === 'desc' ? 'desc' : 'asc');
				$rOrderBy = 'ORDER BY ' . $rOrder[$rOrderRow] . ' ' . $rOrderDirection;
			}
			if (0 < count($rWhere)) {
				$rWhereString = 'WHERE ' . implode(' AND ', $rWhere);
			} else {
				$rWhereString = '';
			}
			$rCountQuery = 'SELECT COUNT(`streams`.`id`) AS `count` FROM `streams` ' . $rWhereString . ';';
			$db->query($rCountQuery, ...$rWhereV);
			if ($db->num_rows() == 1) {
				$rReturn['recordsTotal'] = $db->get_row()['count'];
			} else {
				$rReturn['recordsTotal'] = 0;
			}
			$rReturn['recordsFiltered'] = $rReturn['recordsTotal'];
			if (0 >= $rReturn['recordsTotal']) {
			} else {
				$rQuery = 'SELECT `id`, `stream_icon`, `stream_display_name`, `category_id`, (SELECT COUNT(*) FROM `lines_live` LEFT JOIN `lines` ON `lines`.`id` = `lines_live`.`user_id` WHERE `lines_live`.`stream_id` = `streams`.`id` AND `hls_end` = 0 AND `lines`.`member_id` IN (' . implode(',', $rUserInfo['reports']) . ')) AS `clients` FROM `streams` ' . $rWhereString . ' ' . $rOrderBy . ' LIMIT ' . $rStart . ', ' . $rLimit . ';';
				$db->query($rQuery, ...$rWhereV);
				if (0 >= $db->num_rows()) {
				} else {
					$rRows = $db->get_rows();
					if (!SettingsManager::getAll()['redis_handler']) {
					} else {
						$rConnectionCount = $rReports = array();
						$db->query('SELECT `id` FROM `lines` WHERE `member_id` IN (' . implode(',', $rUserInfo['reports']) . ');');
						foreach ($db->get_rows() as $rRow) {
							$rReports[] = $rRow['id'];
						}
						if (0 >= count($rReports)) {
						} else {
							foreach (ConnectionTracker::getUserConnections($rReports, false) as $rUserID => $rConnections) {
								foreach ($rConnections as $rConnection) {
									$rConnectionCount[$rConnection['stream_id']]++;
								}
							}
						}
					}
					foreach ($rRows as $rRow) {
						if (!SettingsManager::getAll()['redis_handler']) {
						} else {
							$rRow['clients'] = ($rConnectionCount[$rRow['id']] ?: 0);
						}
						if (!$rIsAPI) {
							$rCategoryIDs = json_decode($rRow['category_id'], true);
							if (0 < strlen(RequestManager::getAll()['category'])) {
								$rCategory = ($rCategories[intval(RequestManager::getAll()['category'])]['category_name'] ?: 'No Category');
							} else {
								$rCategory = ($rCategories[$rCategoryIDs[0]]['category_name'] ?: 'No Category');
							}
							if (1 >= count($rCategoryIDs)) {
							} else {
								$rCategory .= ' (+' . (count($rCategoryIDs) - 1) . ' others)';
							}
							if (0 < $rRow['clients']) {
								if ($rPermissions['reseller_client_connection_logs']) {
									$rButtons = '<button title="Kill Connections" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="api(' . $rRow['id'] . ", 'purge');\"><i class=\"mdi mdi-hammer\"></i></button>";
									$rClients = "<a href='javascript: void(0);' onClick='viewLiveConnections(" . intval($rRow['id']) . ");'><button type='button' class='btn btn-info btn-xs waves-effect waves-light'>" . $rRow['clients'] . '</button></a>';
								} else {
									$rButtons = '<button type="button" disabled class="btn btn-light waves-effect waves-light btn-xs"><i class="mdi mdi-hammer"></i></button>';
									$rClients = "<button type='button' class='btn btn-info btn-xs waves-effect waves-light'>" . $rRow['clients'] . '</button>';
								}
							} else {
								$rButtons = '<button type="button" disabled class="btn btn-light waves-effect waves-light btn-xs"><i class="mdi mdi-hammer"></i></button>';
								$rClients = "<button type='button' class='btn btn-secondary btn-xs waves-effect waves-light'>0</button>";
							}
							if (0 < strlen($rRow['stream_icon'])) {
								$rIcon = "<a href='javascript: void(0);' onClick='openImage(this);' data-src='resize?maxw=512&maxh=512&url=" . $rRow['stream_icon'] . "'><img loading='lazy' src='resize?maxw=96&maxh=32&url=" . $rRow['stream_icon'] . "' /></a>";
							} else {
								$rIcon = '';
							}
							$rReturn['data'][] = array($rRow['id'], $rIcon, $rRow['stream_display_name'], $rCategory, $rClients, $rButtons);
						} else {
							$rReturn['data'][] = self::filterRow($rRow, RequestManager::getAll()['show_columns'], RequestManager::getAll()['hide_columns']);
						}
					}
				}
			}
			echo json_encode($rReturn);
			exit();
		}
		echo json_encode($rReturn);
		exit();
	}

	/**
	 * Render the reseller "movies" table.
	 *
	 * @param array  $rReturn      DataTables request/response payload.
	 * @param bool   $rIsAPI       Whether the request came via the API.
	 * @param array  $rUserInfo    Authenticated reseller user.
	 * @param array  $rPermissions Effective permissions.
	 * @param array  $rSettings    Panel settings.
	 * @param \XcVm\Core\Database\DatabaseHandler $db           Database handler.
	 * @param int    $rStart       Pagination offset.
	 * @param int    $rLimit       Page size.
	 * @return void
	 */
	private static function handleMovies(array $rReturn, bool $rIsAPI, array $rUserInfo, array $rPermissions, array $rSettings, $db, int $rStart, int $rLimit): void {
		if (!$rPermissions['can_view_vod']) {
			exit();
		}
		$rCategories = CategoryService::getAllByType('movie');
		$rOrderBy = '';
		$rOrder = array('`id`', false, '`stream_display_name`', '`category_id`', '`clients`', false);
		if (isset(RequestManager::getAll()['order']) && 0 < strlen(RequestManager::getAll()['order'][0]['column'])) {
			$rOrderRow = intval(RequestManager::getAll()['order'][0]['column']);
		} else {
			$rOrderRow = 0;
		}
		$rCreated = isset(RequestManager::getAll()['created']);
		$rWhere = $rWhereV = array();
		if (0 < count($rPermissions['stream_ids'])) {
			$rWhere[] = '`streams`.`id` IN (' . implode(',', array_map('intval', $rPermissions['stream_ids'])) . ')';
			$rWhere[] = '`type` = 2';
			if (0 >= strlen(RequestManager::getAll()['search']['value'])) {
			} else {
				foreach (range(1, 2) as $rInt) {
					$rWhereV[] = '%' . RequestManager::getAll()['search']['value'] . '%';
				}
				$rWhere[] = '(`id` LIKE ? OR `stream_display_name` LIKE ?)';
			}
			if (0 >= strlen(RequestManager::getAll()['category'])) {
			} else {
				$rWhere[] = "JSON_CONTAINS(`category_id`, ?, '\$')";
				$rWhereV[] = RequestManager::getAll()['category'];
			}
			if (!$rOrder[$rOrderRow]) {
			} else {
				$rOrderDirection = (strtolower(RequestManager::getAll()['order'][0]['dir']) === 'desc' ? 'desc' : 'asc');
				$rOrderBy = 'ORDER BY ' . $rOrder[$rOrderRow] . ' ' . $rOrderDirection;
			}
			if (0 < count($rWhere)) {
				$rWhereString = 'WHERE ' . implode(' AND ', $rWhere);
			} else {
				$rWhereString = '';
			}
			$rCountQuery = 'SELECT COUNT(`streams`.`id`) AS `count` FROM `streams` ' . $rWhereString . ';';
			$db->query($rCountQuery, ...$rWhereV);
			if ($db->num_rows() == 1) {
				$rReturn['recordsTotal'] = $db->get_row()['count'];
			} else {
				$rReturn['recordsTotal'] = 0;
			}
			$rReturn['recordsFiltered'] = $rReturn['recordsTotal'];
			if (0 >= $rReturn['recordsTotal']) {
			} else {
				$rQuery = 'SELECT `id`, `stream_icon`, `stream_display_name`, `movie_properties`, `category_id`, (SELECT COUNT(*) FROM `lines_live` LEFT JOIN `lines` ON `lines`.`id` = `lines_live`.`user_id` WHERE `lines_live`.`stream_id` = `streams`.`id` AND `hls_end` = 0 AND `lines`.`member_id` IN (' . implode(',', $rUserInfo['reports']) . ')) AS `clients` FROM `streams` ' . $rWhereString . ' ' . $rOrderBy . ' LIMIT ' . $rStart . ', ' . $rLimit . ';';
				$db->query($rQuery, ...$rWhereV);
				if (0 >= $db->num_rows()) {
				} else {
					$rRows = $db->get_rows();
					if (!SettingsManager::getAll()['redis_handler']) {
					} else {
						$rConnectionCount = $rReports = array();
						$db->query('SELECT `id` FROM `lines` WHERE `member_id` IN (' . implode(',', $rUserInfo['reports']) . ');');
						foreach ($db->get_rows() as $rRow) {
							$rReports[] = $rRow['id'];
						}
						if (0 >= count($rReports)) {
						} else {
							foreach (ConnectionTracker::getUserConnections($rReports, false) as $rUserID => $rConnections) {
								foreach ($rConnections as $rConnection) {
									$rConnectionCount[$rConnection['stream_id']]++;
								}
							}
						}
					}
					foreach ($rRows as $rRow) {
						if (!SettingsManager::getAll()['redis_handler']) {
						} else {
							$rRow['clients'] = ($rConnectionCount[$rRow['id']] ?: 0);
						}
						if (!$rIsAPI) {
							$rCategoryIDs = json_decode($rRow['category_id'], true);
							if (0 < strlen(RequestManager::getAll()['category'])) {
								$rCategory = ($rCategories[intval(RequestManager::getAll()['category'])]['category_name'] ?: 'No Category');
							} else {
								$rCategory = ($rCategories[$rCategoryIDs[0]]['category_name'] ?: 'No Category');
							}
							if (1 >= count($rCategoryIDs)) {
							} else {
								$rCategory .= ' (+' . (count($rCategoryIDs) - 1) . ' others)';
							}
							if (0 < $rRow['clients']) {
								if ($rPermissions['reseller_client_connection_logs']) {
									$rButtons = '<button title="Kill Connections" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="api(' . $rRow['id'] . ", 'purge');\"><i class=\"mdi mdi-hammer\"></i></button>";
									$rClients = "<a href='javascript: void(0);' onClick='viewLiveConnections(" . intval($rRow['id']) . ");'><button type='button' class='btn btn-info btn-xs waves-effect waves-light'>" . $rRow['clients'] . '</button></a>';
								} else {
									$rButtons = '<button type="button" disabled class="btn btn-light waves-effect waves-light btn-xs"><i class="mdi mdi-hammer"></i></button>';
									$rClients = "<button type='button' class='btn btn-info btn-xs waves-effect waves-light'>" . $rRow['clients'] . '</button>';
								}
							} else {
								$rButtons = '<button type="button" disabled class="btn btn-light waves-effect waves-light btn-xs"><i class="mdi mdi-hammer"></i></button>';
								$rClients = "<button type='button' class='btn btn-secondary btn-xs waves-effect waves-light'>0</button>";
							}
							$rProperties = json_decode($rRow['movie_properties'], true);
							if (0 < strlen($rProperties['movie_image'])) {
								$rImage = "<a href='javascript: void(0);' onClick='openImage(this);' data-src='resize?maxw=512&maxh=512&url=" . $rProperties['movie_image'] . "'><img loading='lazy' src='resize?maxh=58&maxw=32&url=" . $rProperties['movie_image'] . "' /></a>";
							} else {
								$rImage = '';
							}
							$rReturn['data'][] = array($rRow['id'], $rImage, $rRow['stream_display_name'], $rCategory, $rClients, $rButtons);
						} else {
							$rReturn['data'][] = self::filterRow($rRow, RequestManager::getAll()['show_columns'], RequestManager::getAll()['hide_columns']);
						}
					}
				}
			}
			echo json_encode($rReturn);
			exit();
		}
		echo json_encode($rReturn);
		exit();
	}

	/**
	 * Render the reseller "episodes" table.
	 *
	 * @param array  $rReturn      DataTables request/response payload.
	 * @param bool   $rIsAPI       Whether the request came via the API.
	 * @param array  $rUserInfo    Authenticated reseller user.
	 * @param array  $rPermissions Effective permissions.
	 * @param array  $rSettings    Panel settings.
	 * @param \XcVm\Core\Database\DatabaseHandler $db           Database handler.
	 * @param int    $rStart       Pagination offset.
	 * @param int    $rLimit       Page size.
	 * @return void
	 */
	private static function handleEpisodes(array $rReturn, bool $rIsAPI, array $rUserInfo, array $rPermissions, array $rSettings, $db, int $rStart, int $rLimit): void {
		if (!$rPermissions['can_view_vod']) {
			exit();
		}
		$rCategories = CategoryService::getAllByType('series');
		$rOrderBy = '';
		$rOrder = array('`id`', false, '`stream_display_name`', '`category_id`', '`clients`', false);
		if (isset(RequestManager::getAll()['order']) && 0 < strlen(RequestManager::getAll()['order'][0]['column'])) {
			$rOrderRow = intval(RequestManager::getAll()['order'][0]['column']);
		} else {
			$rOrderRow = 0;
		}
		$rCreated = isset(RequestManager::getAll()['created']);
		$rWhere = $rWhereV = array();
		if (0 < count($rPermissions['stream_ids'])) {
			$rWhere[] = '`streams`.`id` IN (' . implode(',', array_map('intval', $rPermissions['stream_ids'])) . ')';
			$rWhere[] = '`type` = 5';
			if (0 >= strlen(RequestManager::getAll()['search']['value'])) {
			} else {
				foreach (range(1, 3) as $rInt) {
					$rWhereV[] = '%' . RequestManager::getAll()['search']['value'] . '%';
				}
				$rWhere[] = '(`streams`.`id` LIKE ? OR `stream_display_name` LIKE ? OR `streams_series`.`title` LIKE ?)';
			}
			if (0 >= strlen(RequestManager::getAll()['category'])) {
			} else {
				$rWhere[] = "JSON_CONTAINS(`streams_series`.`category_id`, ?, '\$')";
				$rWhereV[] = RequestManager::getAll()['category'];
			}
			if (0 >= strlen(RequestManager::getAll()['series'])) {
			} else {
				$rWhere[] = '`streams_series`.`id` = ?';
				$rWhereV[] = RequestManager::getAll()['series'];
			}
			if (!$rOrder[$rOrderRow]) {
			} else {
				$rOrderDirection = (strtolower(RequestManager::getAll()['order'][0]['dir']) === 'desc' ? 'desc' : 'asc');
				$rOrderBy = 'ORDER BY ' . $rOrder[$rOrderRow] . ' ' . $rOrderDirection;
			}
			if (0 < count($rWhere)) {
				$rWhereString = 'WHERE ' . implode(' AND ', $rWhere);
			} else {
				$rWhereString = '';
			}
			$rCountQuery = 'SELECT COUNT(`streams`.`id`) AS `count` FROM `streams` LEFT JOIN `streams_episodes` ON `streams_episodes`.`stream_id` = `streams`.`id` LEFT JOIN `streams_series` ON `streams_series`.`id` = `streams_episodes`.`series_id` ' . $rWhereString . ';';
			$db->query($rCountQuery, ...$rWhereV);
			if ($db->num_rows() == 1) {
				$rReturn['recordsTotal'] = $db->get_row()['count'];
			} else {
				$rReturn['recordsTotal'] = 0;
			}
			$rReturn['recordsFiltered'] = $rReturn['recordsTotal'];
			if (0 >= $rReturn['recordsTotal']) {
			} else {
				$rQuery = 'SELECT `streams`.`id`, `stream_icon`, `stream_display_name`, `movie_properties`, `streams_series`.`category_id`, `streams_series`.`title`, `streams_episodes`.`season_num`, (SELECT COUNT(*) FROM `lines_live` LEFT JOIN `lines` ON `lines`.`id` = `lines_live`.`user_id` WHERE `lines_live`.`stream_id` = `streams`.`id` AND `hls_end` = 0 AND `lines`.`member_id` IN (' . implode(',', $rUserInfo['reports']) . ')) AS `clients` FROM `streams` LEFT JOIN `streams_episodes` ON `streams_episodes`.`stream_id` = `streams`.`id` LEFT JOIN `streams_series` ON `streams_series`.`id` = `streams_episodes`.`series_id` ' . $rWhereString . ' ' . $rOrderBy . ' LIMIT ' . $rStart . ', ' . $rLimit . ';';
				$db->query($rQuery, ...$rWhereV);
				if (0 >= $db->num_rows()) {
				} else {
					$rRows = $db->get_rows();
					if (!SettingsManager::getAll()['redis_handler']) {
					} else {
						$rConnectionCount = $rReports = array();
						$db->query('SELECT `id` FROM `lines` WHERE `member_id` IN (' . implode(',', $rUserInfo['reports']) . ');');
						foreach ($db->get_rows() as $rRow) {
							$rReports[] = $rRow['id'];
						}
						if (0 >= count($rReports)) {
						} else {
							foreach (ConnectionTracker::getUserConnections($rReports, false) as $rUserID => $rConnections) {
								foreach ($rConnections as $rConnection) {
									$rConnectionCount[$rConnection['stream_id']]++;
								}
							}
						}
					}
					foreach ($rRows as $rRow) {
						if (!SettingsManager::getAll()['redis_handler']) {
						} else {
							$rRow['clients'] = ($rConnectionCount[$rRow['id']] ?: 0);
						}
						if (!$rIsAPI) {
							$rSeriesName = $rRow['title'] . ' - Season ' . $rRow['season_num'];
							$rStreamName = '<b>' . $rRow['stream_display_name'] . "</b><br><span style='font-size:11px;'>" . $rSeriesName . '</span>';
							$rCategoryIDs = json_decode($rRow['category_id'], true);
							if (0 < strlen(RequestManager::getAll()['category'])) {
								$rCategory = ($rCategories[intval(RequestManager::getAll()['category'])]['category_name'] ?: 'No Category');
							} else {
								$rCategory = ($rCategories[$rCategoryIDs[0]]['category_name'] ?: 'No Category');
							}
							if (1 >= count($rCategoryIDs)) {
							} else {
								$rCategory .= ' (+' . (count($rCategoryIDs) - 1) . ' others)';
							}
							if (0 < $rRow['clients']) {
								if ($rPermissions['reseller_client_connection_logs']) {
									$rButtons = '<button title="Kill Connections" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="api(' . $rRow['id'] . ", 'purge');\"><i class=\"mdi mdi-hammer\"></i></button>";
									$rClients = "<a href='javascript: void(0);' onClick='viewLiveConnections(" . intval($rRow['id']) . ");'><button type='button' class='btn btn-info btn-xs waves-effect waves-light'>" . $rRow['clients'] . '</button></a>';
								} else {
									$rButtons = '<button type="button" disabled class="btn btn-light waves-effect waves-light btn-xs"><i class="mdi mdi-hammer"></i></button>';
									$rClients = "<button type='button' class='btn btn-info btn-xs waves-effect waves-light'>" . $rRow['clients'] . '</button>';
								}
							} else {
								$rButtons = '<button type="button" disabled class="btn btn-light waves-effect waves-light btn-xs"><i class="mdi mdi-hammer"></i></button>';
								$rClients = "<button type='button' class='btn btn-secondary btn-xs waves-effect waves-light'>0</button>";
							}
							$rProperties = json_decode($rRow['movie_properties'], true);
							if (0 < strlen($rProperties['movie_image'])) {
								$rImage = "<a href='javascript: void(0);' onClick='openImage(this);' data-src='resize?maxw=512&maxh=512&url=" . $rProperties['movie_image'] . "'><img loading='lazy' src='resize?maxh=58&maxw=32&url=" . $rProperties['movie_image'] . "' /></a>";
							} else {
								$rImage = '';
							}
							$rReturn['data'][] = array($rRow['id'], $rImage, $rStreamName, $rCategory, $rClients, $rButtons);
						} else {
							$rReturn['data'][] = self::filterRow($rRow, RequestManager::getAll()['show_columns'], RequestManager::getAll()['hide_columns']);
						}
					}
				}
			}
			echo json_encode($rReturn);
			exit();
		}
		echo json_encode($rReturn);
		exit();
	}

	/**
	 * Render the reseller "line activity" table.
	 *
	 * @param array  $rReturn      DataTables request/response payload.
	 * @param bool   $rIsAPI       Whether the request came via the API.
	 * @param array  $rUserInfo    Authenticated reseller user.
	 * @param array  $rPermissions Effective permissions.
	 * @param array  $rSettings    Panel settings.
	 * @param \XcVm\Core\Database\DatabaseHandler $db           Database handler.
	 * @param int    $rStart       Pagination offset.
	 * @param int    $rLimit       Page size.
	 * @return void
	 */
	private static function handleLineActivity(array $rReturn, bool $rIsAPI, array $rUserInfo, array $rPermissions, array $rSettings, $db, int $rStart, int $rLimit): void {
		if (!$rPermissions['reseller_client_connection_logs']) {
			exit();
		}
		$rOrderBy = '';
		$rOrder = array('`username`', '`streams`.`stream_display_name`', '`lines_activity`.`user_agent`', '`lines_activity`.`isp`', '`lines_activity`.`user_ip`', '`lines_activity`.`date_start`', '`lines_activity`.`date_end`', '`lines_activity`.`date_end` - `lines_activity`.`date_start`', '`lines_activity`.`container`');
		if (isset(RequestManager::getAll()['order']) && 0 < strlen(RequestManager::getAll()['order'][0]['column'])) {
			$rOrderRow = intval(RequestManager::getAll()['order'][0]['column']);
		} else {
			$rOrderRow = 0;
		}
		$rWhere = $rWhereV = array();
		$rWhere[] = '`lines`.`member_id` IN (' . implode(',', $rUserInfo['reports']) . ')';
		if (0 >= strlen(RequestManager::getAll()['search']['value'])) {
		} else {
			foreach (range(1, 10) as $rInt) {
				$rWhereV[] = '%' . RequestManager::getAll()['search']['value'] . '%';
			}
			$rWhere[] = '(`lines_activity`.`user_agent` LIKE ? OR `lines_activity`.`user_ip` LIKE ? OR `lines_activity`.`container` LIKE ? OR FROM_UNIXTIME(`lines_activity`.`date_start`) LIKE ? OR FROM_UNIXTIME(`lines_activity`.`date_end`) LIKE ? OR `lines_activity`.`geoip_country_code` LIKE ? OR `lines`.`username` LIKE ? OR `mag_devices`.`mac` LIKE ? OR `enigma2_devices`.`mac` LIKE ? OR `streams`.`stream_display_name` LIKE ?)';
		}
		if (0 >= strlen(RequestManager::getAll()['range'])) {
		} else {
			$rStartTime = substr(RequestManager::getAll()['range'], 0, 10);
			$rEndTime = substr(RequestManager::getAll()['range'], strlen(RequestManager::getAll()['range']) - 10, 10);
			if ($rStartTime = strtotime($rStartTime . ' 00:00:00')) {
			} else {
				$rStartTime = null;
			}
			if ($rEndTime = strtotime($rEndTime . ' 23:59:59')) {
			} else {
				$rEndTime = null;
			}
			if (!($rStartTime && $rEndTime)) {
			} else {
				$rWhere[] = '(`lines_activity`.`date_start` >= ? AND `lines_activity`.`date_end` <= ?)';
				$rWhereV[] = $rStartTime;
				$rWhereV[] = $rEndTime;
			}
		}
		if (0 >= strlen(RequestManager::getAll()['stream'])) {
		} else {
			$rWhere[] = '`lines_activity`.`stream_id` = ?';
			$rWhereV[] = RequestManager::getAll()['stream'];
		}
		if (0 >= strlen(RequestManager::getAll()['user'])) {
		} else {
			$rWhere[] = '`lines`.`member_id` = ?';
			$rWhereV[] = RequestManager::getAll()['user'];
		}
		if (0 >= strlen(RequestManager::getAll()['line'])) {
		} else {
			$rWhere[] = '`lines_activity`.`user_id` = ?';
			$rWhereV[] = RequestManager::getAll()['line'];
		}
		if (0 < count($rWhere)) {
			$rWhereString = 'WHERE ' . implode(' AND ', $rWhere);
		} else {
			$rWhereString = '';
		}
		if (!$rOrder[$rOrderRow]) {
		} else {
			$rOrderDirection = (strtolower(RequestManager::getAll()['order'][0]['dir']) === 'desc' ? 'desc' : 'asc');
			$rOrderBy = 'ORDER BY ' . $rOrder[$rOrderRow] . ' ' . $rOrderDirection;
		}
		$rCountQuery = 'SELECT COUNT(*) AS `count` FROM `lines_activity` LEFT JOIN `lines` ON `lines_activity`.`user_id` = `lines`.`id` LEFT JOIN `streams` ON `lines_activity`.`stream_id` = `streams`.`id` LEFT JOIN `mag_devices` ON `mag_devices`.`user_id` = `lines_activity`.`user_id` LEFT JOIN `enigma2_devices` ON `enigma2_devices`.`user_id` = `lines_activity`.`user_id` ' . $rWhereString . ';';
		$db->query($rCountQuery, ...$rWhereV);
		if ($db->num_rows() == 1) {
			$rReturn['recordsTotal'] = $db->get_row()['count'];
		} else {
			$rReturn['recordsTotal'] = 0;
		}
		$rReturn['recordsFiltered'] = $rReturn['recordsTotal'];
		if (0 >= $rReturn['recordsTotal']) {
		} else {
			$rQuery = 'SELECT `mag_devices`.`mag_id`, `enigma2_devices`.`device_id`, `lines`.`is_e2`, `lines`.`is_mag`, `lines_activity`.`activity_id`, `lines_activity`.`container`, `lines_activity`.`isp`, `lines_activity`.`user_id`, `lines_activity`.`stream_id`, `streams`.`series_no`, `lines_activity`.`server_id`, `lines_activity`.`user_agent`, `lines_activity`.`user_ip`, `lines_activity`.`container`, `lines_activity`.`date_start`, `lines_activity`.`date_end`, `lines_activity`.`geoip_country_code`, IF(`lines`.`is_mag`, `mag_devices`.`mac`, IF(`lines`.`is_e2`, `enigma2_devices`.`mac`, `lines`.`username`)) AS `username`, `streams`.`stream_display_name`, `streams`.`type`, `lines`.`is_restreamer` FROM `lines_activity` LEFT JOIN `lines` ON `lines_activity`.`user_id` = `lines`.`id` LEFT JOIN `streams` ON `lines_activity`.`stream_id` = `streams`.`id` LEFT JOIN `mag_devices` ON `mag_devices`.`user_id` = `lines_activity`.`user_id` LEFT JOIN `enigma2_devices` ON `enigma2_devices`.`user_id` = `lines_activity`.`user_id` ' . $rWhereString . ' ' . $rOrderBy . ' LIMIT ' . $rStart . ', ' . $rLimit . ';';
			$db->query($rQuery, ...$rWhereV);
			if (0 >= $db->num_rows()) {
			} else {
				foreach ($db->get_rows() as $rRow) {
					if (!$rIsAPI) {
						if ($rRow['is_mag']) {
							$rUsername = "<a href='mag?id=" . $rRow['mag_id'] . "'>" . $rRow['username'] . '</a>';
						} else {
							if ($rRow['is_e2']) {
								$rUsername = "<a href='enigma?id=" . $rRow['device_id'] . "'>" . $rRow['username'] . '</a>';
							} else {
								$rUsername = "<a href='line?id=" . $rRow['user_id'] . "'>" . $rRow['username'] . '</a>';
							}
						}
						$rChannel = $rRow['stream_display_name'];
						if (0 < strlen($rRow['geoip_country_code'])) {
							$rGeoCountry = "<img loading='lazy' src='assets/images/countries/" . strtolower($rRow['geoip_country_code']) . ".png'></img> &nbsp;";
						} else {
							$rGeoCountry = '';
						}
						if ($rRow['user_ip']) {
							$rIP = $rGeoCountry . "<a onClick=\"whois('" . $rRow['user_ip'] . "');\" href='javascript: void(0);'>" . $rRow['user_ip'] . '</a>';
						} else {
							$rIP = '';
						}
						if ($rRow['date_start']) {
							$rStart = date($rSettings['datetime_format'], $rRow['date_start']);
						} else {
							$rStart = '';
						}
						if ($rRow['date_end']) {
							$rStop = date($rSettings['datetime_format'], $rRow['date_end']);
						} else {
							$rStop = '';
						}
						$rPlayer = trim(explode('(', $rRow['user_agent'])[0]);
						$rDuration = $rRow['date_end'] - $rRow['date_start'];
						$rColour = 'success';
						if (86400 <= $rDuration) {
							$rDuration = sprintf('%02dd %02dh', $rDuration / 86400, ($rDuration / 3600) % 24);
							$rColour = 'danger';
						} else {
							if (3600 <= $rDuration) {
								if (14400 < $rDuration) {
									$rColour = 'warning';
								} else {
									if (43200 >= $rDuration) {
									} else {
										$rColour = 'danger';
									}
								}
								$rDuration = sprintf('%02dh %02dm', $rDuration / 3600, ($rDuration / 60) % 60);
							} else {
								$rDuration = sprintf('%02dm %02ds', ($rDuration / 60) % 60, $rDuration % 60);
							}
						}
						$rDuration = "<button type='button' class='btn btn-" . $rColour . " btn-xs waves-effect waves-light btn-fixed'>" . $rDuration . '</button>';
						$rReturn['data'][] = array($rUsername, $rChannel, $rPlayer, $rRow['isp'], $rIP, $rStart, $rStop, $rDuration, strtoupper($rRow['container']));
					} else {
						$rReturn['data'][] = self::filterRow($rRow, RequestManager::getAll()['show_columns'], RequestManager::getAll()['hide_columns']);
					}
				}
			}
		}
		echo json_encode($rReturn);
		exit();
	}

	/**
	 * Render the reseller "live connections" table.
	 *
	 * @param array  $rReturn      DataTables payload (by reference).
	 * @param bool   $rIsAPI       Whether the request came via the API.
	 * @param array  $rUserInfo    Authenticated reseller user.
	 * @param array  $rPermissions Effective permissions.
	 * @param array  $rSettings    Panel settings.
	 * @param \XcVm\Core\Database\DatabaseHandler $db           Database handler.
	 * @param int    $rStart       Pagination offset.
	 * @param int    $rLimit       Page size.
	 * @return void
	 */
	private static function handleLiveConnections(array &$rReturn, bool $rIsAPI, array $rUserInfo, array $rPermissions, array $rSettings, $db, int $rStart, int $rLimit): void {
		if (!$rPermissions['reseller_client_connection_logs']) {
			exit();
		}
		$rOrderBy = '';
		$rRows = array();
		if (SettingsManager::getAll()['redis_handler']) {
			$rOrderDirection = (strtolower(RequestManager::getAll()['order'][0]['dir']) === 'desc' ? false : true);
			$rReports = array();
			$rUserID = (0 < intval(RequestManager::getAll()['user']) ? intval(RequestManager::getAll()['user']) : null);
			$rStreamID = (0 < intval(RequestManager::getAll()['stream_id']) ? intval(RequestManager::getAll()['stream_id']) : null);
			if ($rUserID && in_array($rUserID, $rUserInfo['reports'])) {
				$db->query('SELECT `id` FROM `lines` WHERE `member_id` = ?;', $rUserID);
			} else {
				$db->query('SELECT `id` FROM `lines` WHERE `member_id` IN (' . implode(',', $rUserInfo['reports']) . ');');
			}
			foreach ($db->get_rows() as $rRow) {
				$rReports[] = $rRow['id'];
			}
			$rKeys = ConnectionTracker::getUserConnections($rReports, false, true);
			if (!$rOrderDirection) {
			} else {
				$rKeys = array_reverse($rKeys);
			}
			$rKeyCount = count($rKeys);
			foreach (RedisManager::instance()->mGet($rKeys) as $rRow) {
				$rRow = igbinary_unserialize($rRow);
				if (is_array($rRow)) {
					if ($rFilterBefore) {
					} else {
						if (!($rStreamID && $rStreamID != $rRow['stream_id'])) {
							if (in_array($rRow['user_id'], $rReports)) {
							} else {
								$rKeyCount--;
							}
						} else {
							$rKeyCount--;
						}
					}
					$rRow['activity_id'] = $rRow['uuid'];
					$rRow['identifier'] = ($rRow['user_id'] ?: $rRow['hmac_id'] . '_' . $rRow['hmac_identifier']);
					$rRow['active_time'] = time() - $rRow['date_start'];
					$rRow['server_name'] = (ServerRepository::getAll()[$rRow['server_id']]['server_name'] ?: '');
					$rRows[] = $rRow;
				} else {
					$rKeyCount--;
				}
			}
			$rOrder = array('uuid', 'divergence', 'identifier', 'stream_display_name', 'user_agent', 'isp', 'user_ip', 'active_time', 'container', null);
			if (isset(RequestManager::getAll()['order']) && 0 < strlen(RequestManager::getAll()['order'][0]['column'])) {
				$rOrderRow = intval(RequestManager::getAll()['order'][0]['column']);
			} else {
				$rOrderRow = 0;
			}
			if (!$rOrder[$rOrderRow]) {
			} else {
				array_multisort(array_column($rRows, $rOrder[$rOrderRow]), ($rOrderDirection ? SORT_ASC : SORT_DESC), $rRows);
			}
			$rRows = array_slice($rRows, $rStart, $rLimit);
			$rUUIDs = $rStreamIDs = $rUserIDs = array();
			foreach ($rRows as $rRow) {
				if (!$rRow['stream_id']) {
				} else {
					$rStreamIDs[] = intval($rRow['stream_id']);
				}
				if (!$rRow['user_id']) {
				} else {
					$rUserIDs[] = intval($rRow['user_id']);
				}
				if (!$rRow['uuid']) {
				} else {
					$rUUIDs[] = $rRow['uuid'];
				}
			}
			$rStreamNames = $rDivergenceMap = $rSeriesMap = $rUserMap = array();
			if (0 >= count($rUserIDs)) {
			} else {
				$db->query('SELECT `lines`.`id`, `lines`.`is_mag`, `lines`.`is_e2`, `lines`.`is_restreamer`, `lines`.`username`, `mag_devices`.`mag_id`, `enigma2_devices`.`device_id` FROM `lines` LEFT JOIN `mag_devices` ON `mag_devices`.`user_id` = `lines`.`id` LEFT JOIN `enigma2_devices` ON `enigma2_devices`.`user_id` = `lines`.`id` WHERE `lines`.`id` IN (' . implode(',', $rUserIDs) . ');');
				foreach ($db->get_rows() as $rRow) {
					$rUserID = $rRow['id'];
					unset($rRow['id']);
					$rUserMap[$rUserID] = $rRow;
				}
			}
			if (0 >= count($rStreamIDs)) {
			} else {
				$db->query('SELECT `stream_id`, `series_id` FROM `streams_episodes` WHERE `stream_id` IN (' . implode(',', $rStreamIDs) . ');');
				foreach ($db->get_rows() as $rRow) {
					$rSeriesMap[$rRow['stream_id']] = $rRow['series_id'];
				}
				$db->query('SELECT `id`, `type`, `stream_display_name` FROM `streams` WHERE `id` IN (' . implode(',', $rStreamIDs) . ');');
				foreach ($db->get_rows() as $rRow) {
					$rStreamNames[$rRow['id']] = array($rRow['stream_display_name'], $rRow['type']);
				}
			}
			if (0 >= count($rUUIDs)) {
			} else {
				$db->query("SELECT `uuid`, `divergence` FROM `lines_divergence` WHERE `uuid` IN ('" . implode("','", $rUUIDs) . "');");
				foreach ($db->get_rows() as $rRow) {
					$rDivergenceMap[$rRow['uuid']] = $rRow['divergence'];
				}
			}
			$i = 0;
			while ($i < count($rRows)) {
				// ?? (not ?:) so a row whose uuid/stream_id/user_id is absent from
				// the lookup maps falls back to the default without an undefined-key warning.
				$rRows[$i]['divergence'] = ($rDivergenceMap[$rRows[$i]['uuid']] ?? 0);
				$rRows[$i]['series_no'] = ($rSeriesMap[$rRows[$i]['stream_id']] ?? null);
				$rRows[$i]['stream_display_name'] = ($rStreamNames[$rRows[$i]['stream_id']][0] ?? '');
				$rRows[$i]['type'] = ($rStreamNames[$rRows[$i]['stream_id']][1] ?? 1);
				$rRows[$i] = array_merge($rRows[$i], ($rUserMap[$rRows[$i]['user_id']] ?? array()));
				$i++;
			}
			$rReturn['recordsTotal'] = $rKeyCount;
			$rReturn['recordsFiltered'] = ($rIsAPI ? ($rReturn['recordsTotal'] < $rLimit ? $rReturn['recordsTotal'] : $rLimit) : $rReturn['recordsTotal']);
		} else {
			$rOrderDirection = (strtolower(RequestManager::getAll()['order'][0]['dir']) === 'desc' ? 'desc' : 'asc');
			$rOrder = array('`lines_live`.`activity_id`', '`lines_live`.`divergence`', '`username`', '`streams`.`stream_display_name`', '`lines_live`.`user_agent`', '`lines_live`.`isp`', '`lines_live`.`user_ip`', 'UNIX_TIMESTAMP() - `lines_live`.`date_start`', '`lines_live`.`container`', false);
			if (isset(RequestManager::getAll()['order']) && 0 < strlen(RequestManager::getAll()['order'][0]['column'])) {
				$rOrderRow = intval(RequestManager::getAll()['order'][0]['column']);
			} else {
				$rOrderRow = 0;
			}
			$rWhere = $rWhereV = array();
			$rWhere[] = '`hls_end` = 0';
			$rWhere[] = '`lines`.`member_id` IN (' . implode(',', $rUserInfo['reports']) . ')';
			if (0 >= strlen(RequestManager::getAll()['search']['value'])) {
			} else {
				foreach (range(1, 9) as $rInt) {
					$rWhereV[] = '%' . RequestManager::getAll()['search']['value'] . '%';
				}
				$rWhere[] = '(`lines_live`.`user_agent` LIKE ? OR `lines_live`.`user_ip` LIKE ? OR `lines_live`.`container` LIKE ? OR FROM_UNIXTIME(`lines_live`.`date_start`) LIKE ? OR `lines_live`.`geoip_country_code` LIKE ? OR `lines`.`username` LIKE ? OR `mag_devices`.`mac` LIKE ? OR `enigma2_devices`.`mac` LIKE ? OR `streams`.`stream_display_name` LIKE ?)';
			}
			if (0 >= intval(RequestManager::getAll()['stream'])) {
			} else {
				$rWhere[] = '`lines_live`.`stream_id` = ?';
				$rWhereV[] = RequestManager::getAll()['stream'];
			}
			if (0 >= intval(RequestManager::getAll()['user'])) {
			} else {
				$rWhere[] = '`lines`.`member_id` = ?';
				$rWhereV[] = RequestManager::getAll()['user'];
			}
			if (0 >= intval(RequestManager::getAll()['line'])) {
			} else {
				$rWhere[] = '`lines_live`.`user_id` = ?';
				$rWhereV[] = RequestManager::getAll()['line'];
			}
			$rWhereString = 'WHERE ' . implode(' AND ', $rWhere);
			if (!$rOrder[$rOrderRow]) {
			} else {
				$rOrderBy = 'ORDER BY ' . $rOrder[$rOrderRow] . ' ' . $rOrderDirection;
			}
			$rCountQuery = 'SELECT COUNT(*) AS `count` FROM `lines_live` LEFT JOIN `lines` ON `lines_live`.`user_id` = `lines`.`id` LEFT JOIN `streams` ON `lines_live`.`stream_id` = `streams`.`id` LEFT JOIN `mag_devices` ON `mag_devices`.`user_id` = `lines_live`.`user_id` LEFT JOIN `enigma2_devices` ON `enigma2_devices`.`user_id` = `lines_live`.`user_id` ' . $rWhereString . ';';
			$db->query($rCountQuery, ...$rWhereV);
			if ($db->num_rows() == 1) {
				$rReturn['recordsTotal'] = $db->get_row()['count'];
			} else {
				$rReturn['recordsTotal'] = 0;
			}
			$rReturn['recordsFiltered'] = $rReturn['recordsTotal'];
			if (0 >= $rReturn['recordsTotal']) {
			} else {
				$rQuery = 'SELECT `mag_devices`.`mag_id`, `enigma2_devices`.`device_id`, `lines`.`is_e2`, `lines`.`is_mag`, `lines_live`.`activity_id`, `lines_live`.`divergence`, `lines_live`.`user_id`, `lines_live`.`stream_id`, `streams`.`series_no`, `lines`.`is_restreamer`, `lines_live`.`isp`, `lines_live`.`server_id`, `lines_live`.`user_agent`, `lines_live`.`user_ip`, `lines_live`.`container`, `lines_live`.`uuid`, `lines_live`.`date_start`, `lines_live`.`geoip_country_code`, IF(`lines`.`is_mag`, `mag_devices`.`mac`, IF(`lines`.`is_e2`, `enigma2_devices`.`mac`, `lines`.`username`)) AS `username`, `streams`.`stream_display_name`, `streams`.`type` FROM `lines_live` LEFT JOIN `lines` ON `lines_live`.`user_id` = `lines`.`id` LEFT JOIN `streams` ON `lines_live`.`stream_id` = `streams`.`id` LEFT JOIN `mag_devices` ON `mag_devices`.`user_id` = `lines_live`.`user_id` LEFT JOIN `enigma2_devices` ON `enigma2_devices`.`user_id` = `lines_live`.`user_id` ' . $rWhereString . ' ' . $rOrderBy . ' LIMIT ' . $rStart . ', ' . $rLimit . ';';
				$db->query($rQuery, ...$rWhereV);
				if (0 >= $db->num_rows()) {
				} else {
					$rRows = $db->get_rows();
				}
			}
		}
		if (0 >= count($rRows)) {
		} else {
			foreach ($rRows as $rRow) {
				if (!$rIsAPI) {
					if ($rRow['divergence'] <= 50) {
						$rDivergence = '<i class="text-success fas fa-square tooltip" title="' . intval(100 - $rRow['divergence']) . '%"></i>';
					} else {
						if ($rRow['divergence'] <= 80) {
							$rDivergence = '<i class="text-warning fas fa-square tooltip" title="' . intval(100 - $rRow['divergence']) . '%"></i>';
						} else {
							$rDivergence = '<i class="text-danger fas fa-square tooltip" title="' . intval(100 - $rRow['divergence']) . '%"></i>';
						}
					}
					if ($rRow['is_mag']) {
						$rUsername = "<a href='mag?id=" . $rRow['mag_id'] . "'>" . $rRow['username'] . '</a>';
					} else {
						if ($rRow['is_e2']) {
							$rUsername = "<a href='enigma?id=" . $rRow['device_id'] . "'>" . $rRow['username'] . '</a>';
						} else {
							$rUsername = "<a href='line?id=" . $rRow['user_id'] . "'>" . $rRow['username'] . '</a>';
						}
					}
					$rChannel = $rRow['stream_display_name'];
					if (0 < strlen($rRow['geoip_country_code'])) {
						$rGeoCountry = "<img loading='lazy' src='assets/images/countries/" . strtolower($rRow['geoip_country_code']) . ".png'></img> &nbsp;";
					} else {
						$rGeoCountry = '';
					}
					if ($rRow['user_ip']) {
						$rIP = $rGeoCountry . "<a onClick=\"whois('" . $rRow['user_ip'] . "');\" href='javascript: void(0);'>" . $rRow['user_ip'] . '</a>';
					} else {
						$rIP = '';
					}
					$rPlayer = trim(explode('(', $rRow['user_agent'])[0]);
					$rDuration = intval(time()) - intval($rRow['date_start']);
					$rColour = 'success';
					if (86400 <= $rDuration) {
						$rDuration = sprintf('%02dd %02dh', $rDuration / 86400, ($rDuration / 3600) % 24);
						$rColour = 'danger';
					} else {
						if (3600 <= $rDuration) {
							if (14400 < $rDuration) {
								$rColour = 'warning';
							} else {
								if (43200 >= $rDuration) {
								} else {
									$rColour = 'danger';
								}
							}
							$rDuration = sprintf('%02dh %02dm', $rDuration / 3600, ($rDuration / 60) % 60);
						} else {
							$rDuration = sprintf('%02dm %02ds', ($rDuration / 60) % 60, $rDuration % 60);
						}
					}
					$rDuration = "<button type='button' class='btn btn-" . $rColour . " btn-xs waves-effect waves-light btn-fixed'>" . $rDuration . '</button>';
					$rButtons = "<button title=\"Kill Connection\" type=\"button\" class=\"btn btn-light waves-effect waves-light btn-xs tooltip\" onClick=\"api('" . $rRow['uuid'] . "', 'kill');\"><i class=\"fas fa-hammer\"></i></button>";
					$rReturn['data'][] = array($rRow['activity_id'], $rDivergence, $rUsername, $rChannel, $rPlayer, $rRow['isp'], $rIP, $rDuration, strtoupper($rRow['container']), $rButtons);
				} else {
					$rReturn['data'][] = self::filterRow($rRow, RequestManager::getAll()['show_columns'], RequestManager::getAll()['hide_columns']);
				}
			}
		}
		echo json_encode($rReturn);
		exit();
	}

	/**
	 * Render the reseller "registered user logs" table.
	 *
	 * @param array  $rReturn      DataTables payload (by reference).
	 * @param bool   $rIsAPI       Whether the request came via the API.
	 * @param array  $rUserInfo    Authenticated reseller user.
	 * @param array  $rPermissions Effective permissions.
	 * @param array  $rSettings    Panel settings.
	 * @param \XcVm\Core\Database\DatabaseHandler $db           Database handler.
	 * @param int    $rStart       Pagination offset.
	 * @param int    $rLimit       Page size.
	 * @return void
	 */
	private static function handleRegUserLogs(array &$rReturn, bool $rIsAPI, array $rUserInfo, array $rPermissions, array $rSettings, $db, int $rStart, int $rLimit): void {
		$rOrderBy = '';
		$rOrder = array('`users_logs`.`id`', '`users`.`username`', '`users_logs`.`log_id`', '`users_logs`.`type`, `users_logs`.`action`', '`users_logs`.`cost`', '`users_logs`.`credits_after`', '`users_logs`.`date`');
		if (isset(RequestManager::getAll()['order']) && 0 < strlen(RequestManager::getAll()['order'][0]['column'])) {
			$rOrderRow = intval(RequestManager::getAll()['order'][0]['column']);
		} else {
			$rOrderRow = 0;
		}
		$rWhere = $rWhereV = array();
		$rWhere[] = '`users_logs`.`owner` IN (' . implode(',', $rUserInfo['reports']) . ')';
		if (0 >= strlen(RequestManager::getAll()['search']['value'])) {
		} else {
			foreach (range(1, 3) as $rInt) {
				$rWhereV[] = '%' . RequestManager::getAll()['search']['value'] . '%';
			}
			$rWhere[] = '(`users`.`username` LIKE ? OR `users_logs`.`type` LIKE ? OR `users_logs`.`action` LIKE ?)';
		}
		if (0 >= strlen(RequestManager::getAll()['range'])) {
		} else {
			$rStartTime = substr(RequestManager::getAll()['range'], 0, 10);
			$rEndTime = substr(RequestManager::getAll()['range'], strlen(RequestManager::getAll()['range']) - 10, 10);
			if ($rStartTime = strtotime($rStartTime . ' 00:00:00')) {
			} else {
				$rStartTime = null;
			}
			if ($rEndTime = strtotime($rEndTime . ' 23:59:59')) {
			} else {
				$rEndTime = null;
			}
			if (!($rStartTime && $rEndTime)) {
			} else {
				$rWhere[] = '(`users_logs`.`date` >= ? AND `users_logs`.`date` <= ?)';
				$rWhereV[] = $rStartTime;
				$rWhereV[] = $rEndTime;
			}
		}
		if (0 >= strlen(RequestManager::getAll()['reseller'])) {
		} else {
			$rWhere[] = '`users_logs`.`owner` = ?';
			$rWhereV[] = RequestManager::getAll()['reseller'];
		}
		if (0 < count($rWhere)) {
			$rWhereString = 'WHERE ' . implode(' AND ', $rWhere);
		} else {
			$rWhereString = '';
		}
		if (!$rOrder[$rOrderRow]) {
		} else {
			$rOrderDirection = (strtolower(RequestManager::getAll()['order'][0]['dir']) === 'desc' ? 'desc' : 'asc');
			$rOrderBy = 'ORDER BY ' . $rOrder[$rOrderRow] . ' ' . $rOrderDirection;
		}
		$rCountQuery = 'SELECT COUNT(*) AS `count` FROM `users_logs` LEFT JOIN `users` ON `users`.`id` = `users_logs`.`owner` ' . $rWhereString . ';';
		$db->query($rCountQuery, ...$rWhereV);
		if ($db->num_rows() == 1) {
			$rReturn['recordsTotal'] = $db->get_row()['count'];
		} else {
			$rReturn['recordsTotal'] = 0;
		}
		$rReturn['recordsFiltered'] = $rReturn['recordsTotal'];
		if (0 >= $rReturn['recordsTotal']) {
		} else {
			$rPackages = PackageService::getAll();
			$rQuery = 'SELECT `users`.`username`, `users_logs`.`id`, `users_logs`.`owner`, `users_logs`.`type`, `users_logs`.`action`, `users_logs`.`log_id`, `users_logs`.`package_id`, `users_logs`.`cost`, `users_logs`.`credits_after`, `users_logs`.`date`, `users_logs`.`deleted_info` FROM `users_logs` LEFT JOIN `users` ON `users`.`id` = `users_logs`.`owner` ' . $rWhereString . ' ' . $rOrderBy . ' LIMIT ' . $rStart . ', ' . $rLimit . ';';
			$db->query($rQuery, ...$rWhereV);
			if (0 >= $db->num_rows()) {
			} else {
				foreach ($db->get_rows() as $rRow) {
					if (!$rIsAPI) {
						if (in_array($rRow['owner'], array_merge($rPermissions['direct_reports'], array($rUserInfo['id'])))) {
							$rOwner = "<a href='user?id=" . intval($rRow['owner']) . "'>" . $rRow['username'] . '</a>';
						} else {
							$rOwner = "<a href='user?id=" . intval($rRow['owner']) . "'>" . $rRow['username'] . "<br/><small class='text-pink'>(indirect)</small></a>";
						}
						$rDevice = array('line' => 'User Line', 'mag' => 'MAG Device', 'enigma' => 'Enigma2 Device', 'user' => 'Reseller')[$rRow['type']];
						switch ($rRow['action']) {
							case 'new':
								if ($rRow['package_id']) {
									$rText = 'Created New ' . $rDevice . ' with Package: ' . $rPackages[$rRow['package_id']]['package_name'];
								} else {
									$rText = 'Created New ' . $rDevice;
								}
								break;
							case 'extend':
								if ($rRow['package_id']) {
									$rText = 'Extended ' . $rDevice . ' with Package: ' . $rPackages[$rRow['package_id']]['package_name'];
								} else {
									$rText = 'Extended ' . $rDevice;
								}
								break;
							case 'edit':
								$rText = 'Edited ' . $rDevice;
								break;
							case 'enable':
								$rText = 'Enabled ' . $rDevice;
								break;
							case 'disable':
								$rText = 'Disabled ' . $rDevice;
								break;
							case 'delete':
								$rText = 'Deleted ' . $rDevice;
								break;
							case 'send_event':
								$rText = 'Sent Event to ' . $rDevice;
								break;
							case 'adjust_credits':
								$rText = 'Adjusted Credits by ' . $rRow['cost'];
								break;
						}
						$rLineInfo = null;
						switch ($rRow['type']) {
							case 'line':
								$rLine = UserRepository::getLineById($rRow['log_id']);
								if (!$rLine) {
								} else {
									$rLineInfo = "<a href='line?id=" . $rRow['log_id'] . "'>" . $rLine['username'] . '</a>';
								}
								break;
							case 'user':
								$rLine = UserRepository::getRegisteredUserById($rRow['log_id']);
								if (!$rLine) {
								} else {
									$rLineInfo = "<a href='user?id=" . $rRow['log_id'] . "'>" . $rLine['username'] . '</a>';
								}
								break;
							case 'mag':
								$rLine = MagService::getById($rRow['log_id']);
								if (!$rLine) {
								} else {
									$rLineInfo = "<a href='mag?id=" . $rRow['log_id'] . "'>" . $rLine['mac'] . '</a>';
								}
								break;
							case 'enigma':
								$rLine = EnigmaService::getById($rRow['log_id']);
								if (!$rLine) {
								} else {
									$rLineInfo = "<a href='enigma?id=" . $rRow['log_id'] . "'>" . $rLine['mac'] . '</a>';
								}
								break;
						}
						if ($rLineInfo) {
						} else {
							$rDeletedInfo = json_decode($rRow['deleted_info'], true);
							if (is_array($rDeletedInfo)) {
								if (isset($rDeletedInfo['mac'])) {
									$rLineInfo = "<span class='text-secondary'>" . $rDeletedInfo['mac'] . '</span>';
								} else {
									$rLineInfo = "<span class='text-secondary'>" . $rDeletedInfo['username'] . '</span>';
								}
							} else {
								$rLineInfo = "<span class='text-secondary'>DELETED</span>";
							}
						}
						$rReturn['data'][] = array($rRow['id'], $rOwner, $rLineInfo, $rText, number_format($rRow['cost'], 0), number_format($rRow['credits_after'], 0), date($rSettings['datetime_format'], $rRow['date']));
					} else {
						unset($rRow['deleted_info']);
						$rReturn['data'][] = self::filterRow($rRow, RequestManager::getAll()['show_columns'], RequestManager::getAll()['hide_columns']);
					}
				}
			}
		}
		echo json_encode($rReturn);
		exit();
	}

	/**
	 * Render the reseller "registered users" table.
	 *
	 * @param array  $rReturn      DataTables payload (by reference).
	 * @param bool   $rIsAPI       Whether the request came via the API.
	 * @param array  $rUserInfo    Authenticated reseller user.
	 * @param array  $rPermissions Effective permissions.
	 * @param array  $rSettings    Panel settings.
	 * @param \XcVm\Core\Database\DatabaseHandler $db           Database handler.
	 * @param int    $rStart       Pagination offset.
	 * @param int    $rLimit       Page size.
	 * @return void
	 */
	private static function handleRegUsers(array &$rReturn, bool $rIsAPI, array $rUserInfo, array $rPermissions, array $rSettings, $db, int $rStart, int $rLimit): void {
		if (!$rPermissions['create_sub_resellers']) {
			exit();
		}
		$rOrderBy = '';
		$rOrder = array('`users`.`id`', '`users`.`username`', '`r`.`username`', '`users`.`ip`', '`users`.`status`', '`users`.`credits`', '`user_count`', '`users`.`last_login`', false);
		if (isset(RequestManager::getAll()['order']) && 0 < strlen(RequestManager::getAll()['order'][0]['column'])) {
			$rOrderRow = intval(RequestManager::getAll()['order'][0]['column']);
		} else {
			$rOrderRow = 0;
		}
		$rWhere = $rWhereV = array();
		$rWhere[] = '`users`.`owner_id` IN (' . implode(',', $rUserInfo['reports']) . ')';
		if (0 >= strlen(RequestManager::getAll()['search']['value'])) {
		} else {
			foreach (range(1, 9) as $rInt) {
				$rWhereV[] = '%' . RequestManager::getAll()['search']['value'] . '%';
			}
			$rWhere[] = '(`users`.`id` LIKE ? OR `users`.`username` LIKE ? OR `users`.`notes` LIKE ? OR `r`.`username` LIKE ? OR FROM_UNIXTIME(`users`.`date_registered`) LIKE ? OR FROM_UNIXTIME(`users`.`last_login`) LIKE ? OR `users`.`email` LIKE ? OR `users`.`ip` LIKE ? OR `users_groups`.`group_name` LIKE ?)';
		}
		if (0 >= strlen(RequestManager::getAll()['filter'])) {
		} else {
			if (RequestManager::getAll()['filter'] == 1) {
				$rWhere[] = '`users`.`status` = 1';
			} else {
				if (RequestManager::getAll()['filter'] != 2) {
				} else {
					$rWhere[] = '`users`.`status` = 0';
				}
			}
		}
		if (0 >= strlen(RequestManager::getAll()['reseller'])) {
		} else {
			$rWhere[] = '`users`.`owner_id` = ?';
			$rWhereV[] = RequestManager::getAll()['reseller'];
		}
		if (0 < count($rWhere)) {
			$rWhereString = 'WHERE ' . implode(' AND ', $rWhere);
		} else {
			$rWhereString = '';
		}
		if (!$rOrder[$rOrderRow]) {
		} else {
			$rOrderDirection = (strtolower(RequestManager::getAll()['order'][0]['dir']) === 'desc' ? 'desc' : 'asc');
			$rOrderBy = 'ORDER BY ' . $rOrder[$rOrderRow] . ' ' . $rOrderDirection;
		}
		$rCountQuery = 'SELECT COUNT(*) AS `count` FROM `users` LEFT JOIN `users_groups` ON `users_groups`.`group_id` = `users`.`member_group_id` LEFT JOIN `users` AS `r` on `r`.`id` = `users`.`owner_id` ' . $rWhereString . ';';
		$db->query($rCountQuery, ...$rWhereV);
		if ($db->num_rows() == 1) {
			$rReturn['recordsTotal'] = $db->get_row()['count'];
		} else {
			$rReturn['recordsTotal'] = 0;
		}
		$rReturn['recordsFiltered'] = $rReturn['recordsTotal'];
		if (0 >= $rReturn['recordsTotal']) {
		} else {
			$rQuery = 'SELECT `users`.`id`, `users`.`status`, `users_groups`.`is_reseller`, `users`.`notes`, `users`.`owner_id`, `users`.`credits`, `users`.`username`, `users`.`email`, `users`.`ip`, FROM_UNIXTIME(`users`.`date_registered`) AS `date_registered`, FROM_UNIXTIME(`users`.`last_login`) AS `last_login`, `r`.`username` as `owner_username`, `users_groups`.`group_name`, `users`.`status`, (SELECT COUNT(`id`) FROM `lines` WHERE `member_id` = `users`.`id`) AS `user_count` FROM `users` LEFT JOIN `users_groups` ON `users_groups`.`group_id` = `users`.`member_group_id` LEFT JOIN `users` AS `r` on `r`.`id` = `users`.`owner_id` ' . $rWhereString . ' ' . $rOrderBy . ' LIMIT ' . $rStart . ', ' . $rLimit . ';';
			$db->query($rQuery, ...$rWhereV);
			if (0 >= $db->num_rows()) {
			} else {
				foreach ($db->get_rows() as $rRow) {
					if (!$rIsAPI) {
						if ($rRow['status'] == 1) {
							$rStatus = '<i class="text-success fas fa-square"></i>';
						} else {
							$rStatus = '<i class="text-secondary fas fa-square"></i>';
						}
						if ($rRow['last_login']) {
						} else {
							$rRow['last_login'] = 'NEVER';
						}
						$rButtons = '<div class="btn-group">';
						if (0 < strlen($rRow['notes'])) {
							$rButtons .= '<button type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" title="' . $rRow['notes'] . '"><i class="mdi mdi-note"></i></button>';
						} else {
							$rButtons .= '<button disabled type="button" class="btn btn-light waves-effect waves-light btn-xs"><i class="mdi mdi-note"></i></button>';
						}
						if (in_array($rRow['id'], array_merge($rPermissions['direct_reports'], array($rUserInfo['id'])))) {
							$rButtons .= '<button title="Adjust Credits" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="addCredits(' . $rRow['id'] . ", '" . addslashes($rRow['username']) . "', " . intval($rRow['credits']) . ');"><i class="mdi mdi-coin"></i></button>';
							$rUsername = "<a href='user?id=" . intval($rRow['id']) . "'>" . $rRow['username'] . '</a>';
						} else {
							$rButtons .= '<button title="Adjust Credits" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="addCredits(' . $rRow['id'] . ", '" . addslashes($rRow['username']) . "', " . intval($rRow['credits']) . ', true);"><i class="mdi mdi-coin"></i></button>';
							$rUsername = "<a href='user?id=" . intval($rRow['id']) . "'>" . $rRow['username'] . "<br/><small class='text-pink'>(indirect)</small></a>";
						}
						$rButtons .= '<a href="user?id=' . $rRow['id'] . '"><button title="Edit" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip"><i class="mdi mdi-pencil-outline"></i></button></a>';
						if ($rRow['status'] == 1) {
							$rButtons .= '<button title="Disable" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="api(' . $rRow['id'] . ", 'disable');\"><i class=\"mdi mdi-lock\"></i></button>";
						} else {
							$rButtons .= '<button title="Enable" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="api(' . $rRow['id'] . ", 'enable');\"><i class=\"mdi mdi-lock\"></i></button>";
						}
						if (!$rPermissions['delete_users']) {
						} else {
							$rButtons .= '<button title="Delete" type="button" class="btn btn-light waves-effect waves-light btn-xs tooltip" onClick="api(' . $rRow['id'] . ", 'delete');\"><i class=\"mdi mdi-close\"></i></button>";
						}
						$rButtons .= '</div>';
						if (0 < strlen($rRow['ip'])) {
							$rIP = "<a onClick=\"whois('" . $rRow['ip'] . "');\" href='javascript: void(0);'>" . $rRow['ip'] . '</a>';
						} else {
							$rIP = '';
						}
						if ($rRow['is_reseller']) {
							$rCredits = '<button type="button" class="btn btn-info btn-xs waves-effect waves-light">' . number_format($rRow['credits'], 0) . '</button>';
						} else {
							$rCredits = '<button type="button" class="btn btn-secondary btn-xs waves-effect waves-light">-</button>';
						}
						if (0 < $rRow['user_count']) {
							$rUserCount = '<button type="button" class="btn btn-info btn-xs waves-effect waves-light">' . number_format($rRow['user_count'], 0) . '</button>';
						} else {
							$rUserCount = '<button type="button" class="btn btn-secondary btn-xs waves-effect waves-light">0</button>';
						}
						if (in_array($rRow['owner_id'], array_merge($rPermissions['direct_reports'], array($rUserInfo['id'])))) {
							$rOwner = "<a href='user?id=" . intval($rRow['owner_id']) . "'>" . $rRow['owner_username'] . '</a>';
						} else {
							$rOwner = "<a href='user?id=" . intval($rRow['owner_id']) . "'>" . $rRow['owner_username'] . "<br/><small class='text-pink'>(indirect)</small></a>";
						}
						$rReturn['data'][] = array("<a href='user?id=" . intval($rRow['id']) . "'>" . $rRow['id'] . '</a>', $rUsername, $rOwner, $rIP, $rStatus, $rCredits, $rUserCount, $rRow['last_login'], $rButtons);
					} else {
						$rReturn['data'][] = self::filterRow($rRow, RequestManager::getAll()['show_columns'], RequestManager::getAll()['hide_columns']);
					}
				}
			}
		}
		echo json_encode($rReturn);
		exit();
	}

	/**
	 * Filter a row's columns by show/hide lists.
	 *
	 * @param array $rRow  Row data.
	 * @param array $rShow Columns to keep (whitelist), or empty for all.
	 * @param array $rHide Columns to remove (blacklist).
	 * @return array The filtered row.
	 */
	private static function filterRow($rRow, $rShow, $rHide) {
		if ($rShow || $rHide) {
			$rReturn = array();
			foreach (array_keys($rRow) as $rKey) {
				if ($rShow) {
					if (!in_array($rKey, $rShow)) {
					} else {
						$rReturn[$rKey] = $rRow[$rKey];
					}
				} else {
					if (!$rHide) {
					} else {
						if (in_array($rKey, $rHide)) {
						} else {
							$rReturn[$rKey] = $rRow[$rKey];
						}
					}
				}
			}
			return $rReturn;
		} else {
			return $rRow;
		}
	}
}
