<?php
/*
=====================================================
 DataLife Engine - by SoftNews Media Group
-----------------------------------------------------
 https://dle-news.ru/
-----------------------------------------------------
 File: sitecounter.php
-----------------------------------------------------
 Use: lightweight site counter (unique/day, all-time, online)
=====================================================
*/

if( !defined('DATALIFEENGINE') ) {
	header( "HTTP/1.1 403 Forbidden" );
	header ( 'Location: ../../' );
	die( "Hacking attempt!" );
}

if( !function_exists('mt_sitecounter_render_digits') ) {
	function mt_sitecounter_render_digits(int $value, int $minDigits = 5): string {
		if ($value < 0) $value = 0;

		$raw = (string)$value;
		if ($minDigits > 0 && strlen($raw) < $minDigits) {
			$raw = str_pad($raw, $minDigits, '0', STR_PAD_LEFT);
		}

		$out = '';
		$len = strlen($raw);
		for ($i = 0; $i < $len; $i++) {
			$ch = $raw[$i];
			if ($ch < '0' || $ch > '9') $ch = '0';
			$out .= '<span class="mt-digit" aria-hidden="true">' . $ch . '</span>';
		}

		return $out;
	}
}

if( !function_exists('mt_sitecounter_set_template_vars') ) {
	function mt_sitecounter_autoinstall_tables(): bool {
		global $db;

		if (!function_exists('get_vars') || !function_exists('set_vars')) {
			return false;
		}

		$flag = get_vars('sitecounter_installed');
		if ($flag === '1' || $flag === 1 || $flag === true) {
			return true;
		}

		// Attempt install (idempotent). Errors are suppressed to avoid breaking frontend.
		$tblDaily = PREFIX . '_sitecounter_daily';
		$tblDailyIp = PREFIX . '_sitecounter_daily_ip';
		$tblOnline = PREFIX . '_sitecounter_online';

		$db->query(
			"CREATE TABLE IF NOT EXISTS `{$tblDaily}` (\n"
			. "  `day` DATE NOT NULL,\n"
			. "  `uniques` INT(11) UNSIGNED NOT NULL DEFAULT 0,\n"
			. "  `updated` INT(11) UNSIGNED NOT NULL DEFAULT 0,\n"
			. "  PRIMARY KEY (`day`)\n"
			. ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4",
			false
		);

		$db->query(
			"CREATE TABLE IF NOT EXISTS `{$tblDailyIp}` (\n"
			. "  `day` DATE NOT NULL,\n"
			. "  `ip` VARBINARY(16) NOT NULL,\n"
			. "  `first_seen` INT(11) UNSIGNED NOT NULL DEFAULT 0,\n"
			. "  PRIMARY KEY (`day`, `ip`),\n"
			. "  KEY `day` (`day`)\n"
			. ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4",
			false
		);

		$db->query(
			"CREATE TABLE IF NOT EXISTS `{$tblOnline}` (\n"
			. "  `ip` VARBINARY(16) NOT NULL,\n"
			. "  `first_seen` INT(11) UNSIGNED NOT NULL DEFAULT 0,\n"
			. "  `last_seen` INT(11) UNSIGNED NOT NULL DEFAULT 0,\n"
			. "  PRIMARY KEY (`ip`),\n"
			. "  KEY `last_seen` (`last_seen`)\n"
			. ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4",
			false
		);

		$ok = true;
		foreach (array($tblDaily, $tblDailyIp, $tblOnline) as $t) {
			$db->query("SHOW TABLES LIKE '{$t}'", false);
			if ($db->num_rows() < 1) {
				$ok = false;
				break;
			}
			$db->free();
		}
		$db->free();

		set_vars('sitecounter_installed', $ok ? '1' : '0');
		return $ok;
	}

	function mt_sitecounter_set_template_vars($tpl): void {
		global $db, $config, $_TIME;

		// Fail-safe: if tables are not installed yet, do not break page rendering.
		static $tablesOk = null;
		if ( $tablesOk === null ) {
			mt_sitecounter_autoinstall_tables();

			$tablesOk = true;
			$required = array(
				PREFIX . '_sitecounter_daily',
				PREFIX . '_sitecounter_daily_ip',
				PREFIX . '_sitecounter_online'
			);
			foreach ($required as $t) {
				$db->query("SHOW TABLES LIKE '{$t}'", false);
				if ( $db->num_rows() < 1 ) {
					$tablesOk = false;
					break;
				}
				$db->free();
			}
			$db->free();
		}

		if ( !$tablesOk ) {
			$tpl->set('{sitecounter-today}', '0');
			$tpl->set('{sitecounter-alltime}', '0');
			$tpl->set('{sitecounter-online}', '0');
			$tpl->set('{sitecounter-today-digits}', mt_sitecounter_render_digits(0, 5));
			$tpl->set('{sitecounter-alltime-digits}', mt_sitecounter_render_digits(0, 5));
			$tpl->set('{sitecounter-online-digits}', mt_sitecounter_render_digits(0, 3));
			return;
		}

		$day = date('Y-m-d', $_TIME);
		$todayUniques = 0;
		$allTimeUniques = 0;
		$onlineNow = 0;

		$skipWrites = false;
		if (function_exists('isBotDetected') && isBotDetected()) {
			$skipWrites = true;
		}

		$ip = function_exists('get_ip') ? get_ip() : '';
		$ipBin = ($ip && $ip !== 'not detected') ? @inet_pton($ip) : false;
		if ($ipBin === false) {
			$skipWrites = true;
		}

		// Write path is throttled via cookies to keep DB load low.
		if ( !$skipWrites ) {
			$cookiePrefix = 'mt_sc_';
			$uniqueCookie = $cookiePrefix . 'u_' . $day;
			$pingCookie = $cookiePrefix . 'p';
			$didWriteDaily = false;

			// 1) Unique visitor per day (by IP)
			if ( empty($_COOKIE[$uniqueCookie]) ) {
				$ipSql = $db->safesql($ipBin);
				$db->query("INSERT IGNORE INTO " . PREFIX . "_sitecounter_daily_ip (day, ip, first_seen) VALUES ('{$day}', '{$ipSql}', '{$_TIME}')");
				if ( $db->get_affected_rows() > 0 ) {
					$db->query("INSERT INTO " . PREFIX . "_sitecounter_daily (day, uniques, updated) VALUES ('{$day}', 1, '{$_TIME}') ON DUPLICATE KEY UPDATE uniques=uniques+1, updated='{$_TIME}'");
					$didWriteDaily = true;
				} else {
					$db->query("INSERT IGNORE INTO " . PREFIX . "_sitecounter_daily (day, uniques, updated) VALUES ('{$day}', 0, '{$_TIME}')");
				}

				@setcookie($uniqueCookie, '1', $_TIME + 86400, '/', '', isSSL(), true);
			}

			// 2) Online visitors (by IP), ping at most once / 120s
			$lastPing = isset($_COOKIE[$pingCookie]) ? intval($_COOKIE[$pingCookie]) : 0;
			if ( $_TIME - $lastPing >= 120 ) {
				$ipSql = $db->safesql($ipBin);
				$db->query("INSERT INTO " . PREFIX . "_sitecounter_online (ip, first_seen, last_seen) VALUES ('{$ipSql}', '{$_TIME}', '{$_TIME}') ON DUPLICATE KEY UPDATE last_seen='{$_TIME}'");
				@setcookie($pingCookie, (string)$_TIME, $_TIME + 86400, '/', '', isSSL(), true);
			}

			// 3) Periodic prune of stale online rows (every ~5m)
			$pruneCache = dle_cache('sitecounter', 'prune', true, '5m');
			if ( $pruneCache === false ) {
				$cutoff = $_TIME - 900; // 15 minutes
				$db->query("DELETE FROM " . PREFIX . "_sitecounter_online WHERE last_seen < '{$cutoff}'");
				create_cache('sitecounter', '1', 'prune', true);
			}

			// If we inserted a new unique today, stats cache for today may be stale.
			if ( $didWriteDaily ) {
				clear_cache(array('sitecounter_' . md5('stats_' . $day)));
			}
		}

		// Read path: cache for 1 minute (online will be slightly delayed, but low load).
		$cacheKey = 'stats_' . $day;
		$cached = dle_cache('sitecounter', $cacheKey, true, '1m');
		if ( $cached !== false ) {
			$data = json_decode($cached, true);
			if (json_last_error() === JSON_ERROR_NONE && is_array($data)) {
				$todayUniques = isset($data['today']) ? intval($data['today']) : 0;
				$allTimeUniques = isset($data['all']) ? intval($data['all']) : 0;
				$onlineNow = isset($data['online']) ? intval($data['online']) : 0;
			}
		}

		if ( $cached === false ) {
			$row = $db->super_query("SELECT uniques FROM " . PREFIX . "_sitecounter_daily WHERE day='{$day}'");
			$todayUniques = isset($row['uniques']) ? intval($row['uniques']) : 0;

			$row = $db->super_query("SELECT COALESCE(SUM(uniques), 0) AS total FROM " . PREFIX . "_sitecounter_daily");
			$allTimeUniques = isset($row['total']) ? intval($row['total']) : 0;

			$onlineCutoff = $_TIME - 300; // 5 minutes
			$row = $db->super_query("SELECT COUNT(*) AS cnt FROM " . PREFIX . "_sitecounter_online WHERE last_seen > '{$onlineCutoff}'");
			$onlineNow = isset($row['cnt']) ? intval($row['cnt']) : 0;

			create_cache(
				'sitecounter',
				json_encode(
					array('today' => $todayUniques, 'all' => $allTimeUniques, 'online' => $onlineNow),
					JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
				),
				$cacheKey,
				true
			);
		}

		$tpl->set('{sitecounter-today}', (string)$todayUniques);
		$tpl->set('{sitecounter-alltime}', (string)$allTimeUniques);
		$tpl->set('{sitecounter-online}', (string)$onlineNow);
		$tpl->set('{sitecounter-today-digits}', mt_sitecounter_render_digits($todayUniques, 5));
		$tpl->set('{sitecounter-alltime-digits}', mt_sitecounter_render_digits($allTimeUniques, 5));
		$tpl->set('{sitecounter-online-digits}', mt_sitecounter_render_digits($onlineNow, 3));
	}
}
