<?php
namespace Modules\Admin\Server\Logs;

class ModelGenerateReportLoginStats
{
	public function generate($outputFile, $listingParams)
	{
		$model = new Model();
		$listingParams->setCurrentPage(0);
		$listingParams->setResultsPerPage(PHP_INT_MAX);

		$model->list_execute($listingParams);
		$listRows = $model->list_getRecordsList();

		$fileHandle = fopen($outputFile, 'w');

		fwrite($fileHandle, <<<'HTML'
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>TINA auth logs</title>

	<script src="https://code.jquery.com/jquery-3.6.0.slim.min.js"></script>

	<style>
		pre.output {
			margin-top: 2em;
		}

		.autoselect {
			cursor: pointer;
			user-select: all;
		}
		.noselect {
			user-select: none;
			cursor: default;
		}

		span.id {
			float: right;
			color: #88f;
			padding: 0 0.5em;
		}
		span.addTime {
			float: right;
			color: #888;
			padding: 0 0.5em;
		}
		div.line {
			line-height: 1;
			border-bottom: 1px solid #444;
			padding-bottom: 0.5em;
		}
		div.line >div.message >span.IP {
			color: hsl(5, 75%, 40%);
		}
		div.line >div.message >span.username {
			color: hsl(55, 75%, 30%);
		}

		div.HTTP_USER_AGENT >div.HTTP_USER_AGENT {
			color: hsl(180, 50%, 25%);
		}

		section.log-header {
			position: fixed;
			width: 100%;
			top: 0;
			left: 0;
			background-color: hsl(180,  25%, 95%);
			border-bottom: 2px solid hsl(180,  25%, 90%);
			z-index: 1;
		}

		.log-header >#stats {
			float: right;
			margin-left: 0.5em;
			padding-left: 0.5em;
			border-left: 1px solid #ccc;

			font-size: 0.75em;
			color: hsl(180,  25%, 33%);
		}
	</style>
</head>
<body>

<section class="log-header">
	<span 	id="stats">loading ...</span>
		<button onClick="deleteDuplicates()">delete duplicates</button>
	<button onClick="sortByDate()">sort everything by date</button>

	<script>
		function deleteDuplicates() {
			let uniqueHashes = [...new Set($('pre.output >div.line').map((i, a) => {
				return $(a).attr('data-hash');
			}))];

			let newOutput = '';
			uniqueHashes.forEach((h) => {
				newOutput+= $(`pre.output >div.line[data-hash=${h}]`)[0].outerHTML;
			});
			// seems it's faster to remove the whole element and add it back
			$(`pre.output`).remove();
			$('.log-header').after(`<pre class="output">${newOutput}</pre>`);

			generateStats();
		}

		function sortByDate() {
			let rows = $('pre.output >div.line')
				.sort( (a, b) => {
					let a1 = $(a).find('span.addTime').html();
					let b1 = $(b).find('span.addTime').html();

					if (a1==null) {
						a1 = '';
					}

					if (b1==null) {
						b1 = '';
					}

					return a1.localeCompare(b1);
				}
			);

			$('pre.output').append(rows);
		}

		function generateStats() {
			let totalRows = new Intl.NumberFormat().format($('pre.output >div.line').length);
			$('#stats').html(`got ${totalRows} rows`);
		}

		$(window).on( "load", function() {
			generateStats();
		});
	</script>
</section>

<pre class="output">
HTML
);


		$rowIndex = 0;
		$reportedGroupedData = [
			'HTTP_USER_AGENT' => [],
			'authCredentials.username' => [],
			'REMOTE_ADDR' => [],
		];
		$JSONReport = [
			'rawData' => [],
		];

		foreach ($listRows as $listRow) {
			if (!preg_match("/^\[Modules\\\\Authenticate\\\\Controller::actionIndex\].+/", $listRow['message'])) {
				continue;
			}

			$logRow = $model->getFullLogDetails($listRow['id']);
			if (!$logRow) {
				continue;
			}
			if (!$logRow['context']) {
				continue;
			}

			$SERVER_value = ($logRow['context']['$_SERVER']?$logRow['context']['$_SERVER']->getValue():[]);
			$messageHash = md5($listRow['message'] . $logRow['context']['authCredentials.username'] . $SERVER_value['HTTP_USER_AGENT'] . $SERVER_value['REMOTE_ADDR']);

			$reportedGroupedData['HTTP_USER_AGENT'][$SERVER_value['HTTP_USER_AGENT']]++;
			$reportedGroupedData['authCredentials.username'][$logRow['context']['authCredentials.username']]++;
			$reportedGroupedData['REMOTE_ADDR'][$SERVER_value['REMOTE_ADDR']]++;

			$JSONReport['rawData'][] = [
				'addTime' => $listRow['addTime'],
				'HTTP_USER_AGENT' => $SERVER_value['HTTP_USER_AGENT'],
				'authCredentials.username' => $logRow['context']['authCredentials.username'],
				'REMOTE_ADDR' => $SERVER_value['REMOTE_ADDR'],
			];

			$colorIndex = strlen($listRow['message']??'') + crc32($logRow['context']['authCredentials.username']??'');
			$colorIndex = (round($colorIndex/10) * 10) % 360;

			$fileContent = sprintf("<div style='background-color: hsl(%d, %d%%, %d%%);' class='line %s' data-hash='%s' ><span class='addTime'>%s</span><span class='id'>%s</span><div class='message'>%s</div><div class='data'>%s</div></div>",
				$colorIndex, 66, 92,
				$logRow['context']['$errorLevel'],
				$messageHash,
				$listRow['addTime'],
				$listRow['id'],
				$this->cleanupMessage($listRow['message'], $logRow['context']['authCredentials.username']),
				$this->extractBrowserInfo($logRow),
			);

			fwrite($fileHandle, $fileContent);
			$rowIndex++;
		}

		fwrite($fileHandle, <<<HTML
		</pre>
HTML
		);


		fwrite($fileHandle, '<pre class="stats">');

		fwrite($fileHandle, '<div class="HTTP_USER_AGENT">');
		fwrite($fileHandle, '<h2>HTTP_USER_AGENT</h2>');
		uksort($reportedGroupedData['HTTP_USER_AGENT'], function($a, $b) {
			return strcasecmp($b, $a);
		});
		foreach ($reportedGroupedData['HTTP_USER_AGENT'] as $UA=>$count) {
			fwrite($fileHandle, sprintf("<div class='HTTP_USER_AGENT'><span class='autoselect'>%s</span> <span class='count'>(%d times)</span> <a target='_blank' href='%s'>online check</a></div>",
				$UA,
				$count,
				"https://udger.com/resources/online-parser?" . http_build_query([
					'action' => 'analyze',
					'Fheaders' => $UA,
					'ip' => '',
					'test' => '2280',
				])
			));
		}
		fwrite($fileHandle, '</div>');


		fwrite($fileHandle, '<div class="username">');
		fwrite($fileHandle, '<h2>username</h2>');
		uksort($reportedGroupedData['authCredentials.username'], function($a, $b) {
			return strcasecmp($b, $a);
		});
		foreach ($reportedGroupedData['authCredentials.username'] as $username=>$count) {
			fwrite($fileHandle, sprintf("<div class='username'>%s <span class='count'>(%d times)</span></div>", $username, $count));
		}
		fwrite($fileHandle, '</div>');



		fwrite($fileHandle, '<div class="REMOTE_ADDR">');
		fwrite($fileHandle, '<h2>REMOTE_ADDR</h2>');
		uksort($reportedGroupedData['REMOTE_ADDR'], function($a, $b) {
			return strnatcmp($b, $a);
		});
		foreach ($reportedGroupedData['REMOTE_ADDR'] as $REMOTE_ADDR=>$count) {
			fwrite($fileHandle, sprintf("<div class='REMOTE_ADDR'>%s <span class='count'>(%d times)</span><a target='_blank' href='%s'>online check</a></div>",
				$REMOTE_ADDR,
				$count,
				"https://www.abuseipdb.com/check/".$REMOTE_ADDR
			));
		}
		fwrite($fileHandle, '</div>');


		fwrite($fileHandle, sprintf('<a download="loginsStats.json" href="data:application/json;base64,%s" style="font-size: 2em; margin-top: 5em; display: block;">download report data</a>', base64_encode(json_encode($JSONReport))));

		fwrite($fileHandle, <<<HTML
</body>
</html>
HTML
	);

		fclose($fileHandle);

		return $this;
	}

	/**
	 * Filter the log text
	 * @param $logStr
	 * @return string
	 */
	protected function cleanupMessage($message, $username)
	{
		$message = preg_replace("/(IP:[0-9\.]+)/", "<span class='IP'>$1</span>", $message);
		$message.= sprintf(" (<span class='username'>%s</span>)", $username);
		return $message;
	}

	protected function extractBrowserInfo($logRow)
	{
		$SERVER_value = ($logRow['context']['$_SERVER']?$logRow['context']['$_SERVER']->getValue():[]);
		return sprintf("<div class=''>%s</div>", $SERVER_value['HTTP_USER_AGENT']);
	}

}
