<?php

namespace SHServ\Middleware;

/**
 * SqlQueryLogger — логирует каждый SQL-запрос: текст, длительность (мс),
 * и помечает медленные запросы (>1000 мс) как WARN.
 *
 * Работает через события ThinBuilder:
 *   - module:ThinBuilder.ready_sql → фиксируем SQL + стартовое время
 *   - module:ThinBuilder.query    → вычисляем длительность, пишем лог
 *
 * Поддерживает gen_sql_only (orphan SQL остаётся в pending до конца запроса).
 */
class SqlQueryLogger {
	protected static array $pending = [];

	public function __construct() {
		events() -> handler('module:ThinBuilder.ready_sql', function(Array $params) {
			$sql = $params['sql'] ?? '';
			if ($sql === '') {
				return;
			}
			self::$pending[] = ['sql' => $sql, 'start' => microtime(true)];
		});

		events() -> handler('module:ThinBuilder.query', function(Array $params) {
			$sql    = $params['sql'] ?? '';
			$result = $params['result'] ?? null;

			if ($sql === '') {
				return;
			}

			$start = null;
			foreach (self::$pending as $i => $item) {
				if ($item['sql'] === $sql) {
					$start = $item['start'];
					unset(self::$pending[$i]);
					break;
				}
			}

			if ($start === null) {
				return;
			}

			$duration_ms = round((microtime(true) - $start) * 1000, 3);
			$level       = $duration_ms > 1000 ? 'WARN' : 'INFO';
			$message     = $duration_ms > 1000 ? 'Slow query' : 'Query executed';

			$row_count = null;
			if (is_array($result)) {
				$row_count = count($result);
			} elseif (is_int($result) || is_numeric($result)) {
				$row_count = (int) $result;
			} elseif (is_bool($result)) {
				$row_count = $result ? 1 : 0;
			}

			$context = [
				'sql'         => $sql,
				'duration_ms' => $duration_ms,
			];

			if ($row_count !== null) {
				$context['row_count'] = $row_count;
			}

			logging() -> log($level, 'php:ThinBuilder', $message, $context);
		});

		// Очистка orphan SQL (например, при gen_sql_only)
		events() -> handler('kernel:Bootstrap.app_finished', function(Array $params) {
			if (empty(self::$pending)) {
				return;
			}
			foreach (self::$pending as $item) {
				logging() -> info('php:ThinBuilder', 'Query planned but not executed', [
					'sql' => $item['sql'],
				]);
			}
			self::$pending = [];
		});
	}
}
