<?php
// includes/core/ErrorLogger.php

require_once __DIR__ . '/Database.php';

class ErrorLogger {
    private static $config = null;

    // Defines priority for each level (0 = low, 4 = critical)
    private static $levelPriority = [
        'DEBUG' => 0,
        'INFO'  => 1,
        'WARN'  => 2,
        'ERROR' => 3,
        'FATAL' => 4
    ];

    // Load configuration from config.php once
    private static function loadConfig() {
        if (self::$config === null) {
            $configPath = __DIR__ . '/../config.php';
            if (file_exists($configPath)) {
                self::$config = include $configPath;
            } else {
                self::$config = [];
            }
        }
    }

    // Read configured minimum log level
    private static function getConfiguredLevel() {
        self::loadConfig();
        $level = self::$config['logging']['level'] ?? 'ERROR';
        return strtoupper($level);
    }

    // Check if current level should be logged
    private static function shouldLogLevel($level) {
        $configured = self::getConfiguredLevel();
        return self::$levelPriority[$level] >= self::$levelPriority[$configured];
    }

    // Public generic logger
    public static function log($message, $level = 'INFO') {
        $level = strtoupper($level);
        if (!self::shouldLogLevel($level)) {
            return;
        }

        self::logToDatabase($level, (string)$message);
    }

    // SQL success logger (backward compatible for multiple call styles)
    public static function logSqlSuccess(...$parts) {
        if (self::shouldLogLevel('INFO')) {
            self::logToDatabase('INFO', self::stringifyLogParts($parts));
        }
    }

    // SQL error logger (backward compatible for multiple call styles)
    public static function logSqlError(...$parts) {
        if (self::shouldLogLevel('ERROR')) {
            self::logToDatabase('ERROR', self::stringifyLogParts($parts));
        }
    }

    // Normalize arbitrary log arguments into a stable text message.
    private static function stringifyLogParts(array $parts): string {
        if (count($parts) === 0) {
            return '';
        }

        if (count($parts) === 1 && is_scalar($parts[0])) {
            return (string)$parts[0];
        }

        $normalized = [];
        foreach ($parts as $part) {
            if (is_null($part)) {
                $normalized[] = 'null';
                continue;
            }

            if (is_bool($part)) {
                $normalized[] = $part ? 'true' : 'false';
                continue;
            }

            if (is_scalar($part)) {
                $normalized[] = (string)$part;
                continue;
            }

            if ($part instanceof Throwable) {
                $normalized[] = get_class($part) . ': ' . $part->getMessage();
                continue;
            }

            $json = json_encode($part, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            $normalized[] = ($json === false) ? '[unserializable]' : $json;
        }

        return implode(' | ', $normalized);
    }

    // Write entry to DB
    private static function logToDatabase($level, $message) {
        try {
            $pdo = Database::getInstance();

            // Keep table size bounded
            $countStmt = $pdo->query('SELECT COUNT(*) as total FROM system_logs');
            $count = (int)($countStmt->fetch()['total'] ?? 0);

            if ($count > 1000) {
                $toDelete = $count - 1000;
                $pdo->exec("DELETE FROM system_logs ORDER BY created_at ASC LIMIT $toDelete");
            }

            $stmt = $pdo->prepare(
                'INSERT INTO system_logs (log_level, message, created_at, ip_address)
                 VALUES (:level, :message, NOW(), :ip)'
            );
            $stmt->execute([
                ':level' => $level,
                ':message' => (string)$message,
                ':ip' => $_SERVER['REMOTE_ADDR'] ?? 'CLI'
            ]);
        } catch (Exception $e) {
            // Ignore logging failures to avoid recursive crashes
        }
    }

    // Global uncaught exception handler
    public static function exceptionHandler($exception) {
        self::log('Uncaught Exception: ' . $exception->getMessage(), 'FATAL');
    }

    // PHP error handler
    public static function errorHandler($errno, $errstr, $errfile, $errline) {
        self::log("Error [$errno] in $errfile at line $errline: $errstr", 'ERROR');
        return false;
    }
}

// Register global handlers
set_exception_handler(['ErrorLogger', 'exceptionHandler']);
set_error_handler(['ErrorLogger', 'errorHandler']);
