<?php
/**
 * Error Handler Middleware for Ton Sui Mining
 * 
 * Provides centralized error and exception handling with logging
 */

class ErrorHandler {
    // Error severity levels
    public const DEBUG = 100;
    public const INFO = 200;
    public const NOTICE = 250;
    public const WARNING = 300;
    public const ERROR = 400;
    public const CRITICAL = 500;
    public const ALERT = 550;
    public const EMERGENCY = 600;
    
    // Log levels as strings
    private static $levels = [
        self::DEBUG     => 'DEBUG',
        self::INFO      => 'INFO',
        self::NOTICE    => 'NOTICE',
        self::WARNING   => 'WARNING',
        self::ERROR     => 'ERROR',
        self::CRITICAL  => 'CRITICAL',
        self::ALERT     => 'ALERT',
        self::EMERGENCY => 'EMERGENCY',
    ];
    
    // Configuration
    private static $config = [
        'log_errors' => true,
        'display_errors' => false,
        'log_file' => __DIR__ . '/../../logs/app.log',
        'max_log_files' => 7, // Keep logs for 7 days
        'json_errors' => true, // Return JSON errors for API
        'log_level' => self::DEBUG, // Default log level
    ];
    
    /**
     * Initialize error handling
     */
    public static function init($config = []) {
        // Merge with default config
        self::$config = array_merge(self::$config, $config);
        
        // Set error reporting
        error_reporting(E_ALL);
        
        // Set error and exception handlers
        set_error_handler([__CLASS__, 'handleError']);
        set_exception_handler([__CLASS__, 'handleException']);
        register_shutdown_function([__CLASS__, 'handleShutdown']);
        
        // Set display_errors
        ini_set('display_errors', self::$config['display_errors'] ? '1' : '0');
        
        // Ensure log directory exists
        $logDir = dirname(self::$config['log_file']);
        if (!is_dir($logDir)) {
            mkdir($logDir, 0755, true);
        }
    }
    
    /**
     * Handle PHP errors
     */
    public static function handleError($level, $message, $file = '', $line = 0, $context = []) {
        if (!(error_reporting() & $level)) {
            return false; // Respect error_reporting level
        }
        
        $error = new ErrorException($message, 0, $level, $file, $line);
        self::log($level, $error->getMessage(), [
            'file' => $file,
            'line' => $line,
            'context' => $context,
            'trace' => $error->getTraceAsString()
        ]);
        
        return true;
    }
    
    /**
     * Handle uncaught exceptions
     */
    public static function handleException($exception) {
        $statusCode = $exception->getCode() >= 400 ? $exception->getCode() : 500;
        
        // Log the exception
        self::log(self::ERROR, $exception->getMessage(), [
            'file' => $exception->getFile(),
            'line' => $exception->getLine(),
            'trace' => $exception->getTraceAsString(),
            'code' => $exception->getCode(),
            'class' => get_class($exception)
        ]);
        
        // Send response
        self::sendErrorResponse($statusCode, $exception->getMessage(), [
            'error' => [
                'code' => $exception->getCode(),
                'message' => $exception->getMessage(),
                'file' => $exception->getFile(),
                'line' => $exception->getLine(),
            ]
        ]);
    }
    
    /**
     * Handle PHP shutdown (fatal errors)
     */
    public static function handleShutdown() {
        $error = error_get_last();
        if ($error && in_array($error['type'], [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR])) {
            self::log(self::CRITICAL, $error['message'], [
                'file' => $error['file'],
                'line' => $error['line'],
                'type' => $error['type'],
                'is_fatal' => true
            ]);
            
            self::sendErrorResponse(500, 'Internal Server Error', [
                'error' => [
                    'message' => 'A fatal error occurred',
                    'file' => $error['file'],
                    'line' => $error['line']
                ]
            ]);
        }
    }
    
    /**
     * Log a message
     */
    public static function log($level, $message, $context = []) {
        // Skip if log level is below threshold
        if ($level < self::$config['log_level']) {
            return;
        }
        
        $levelName = self::getLevelName($level);
        $timestamp = date('Y-m-d H:i:s');
        $contextStr = !empty($context) ? json_encode($context, JSON_PRETTY_PRINT) : '';
        
        $logMessage = sprintf(
            "[%s] %s: %s %s" . PHP_EOL,
            $timestamp,
            $levelName,
            $message,
            $contextStr
        );
        
        // Rotate logs if needed
        self::rotateLogs();
        
        // Write to log file
        file_put_contents(
            self::$config['log_file'],
            $logMessage,
            FILE_APPEND | LOCK_EX
        );
    }
    
    /**
     * Rotate log files
     */
    private static function rotateLogs() {
        $logFile = self::$config['log_file'];
        $maxFiles = self::$config['max_log_files'];
        
        // Skip if max_files is 0 (no rotation) or log file doesn't exist
        if ($maxFiles < 1 || !file_exists($logFile)) {
            return;
        }
        
        // Rotate if file is larger than 10MB
        if (filesize($logFile) > 10 * 1024 * 1024) {
            // Close any open file handles
            if (isset(self::$logFileHandle)) {
                fclose(self::$logFileHandle);
                unset(self::$logFileHandle);
            }
            
            // Rotate files
            for ($i = $maxFiles - 1; $i >= 0; $i--) {
                $src = $i === 0 ? $logFile : "{$logFile}.{$i}";
                $dst = "{$logFile}." . ($i + 1);
                
                if (file_exists($src)) {
                    if ($i === $maxFiles - 1) {
                        // Delete the oldest log file
                        @unlink($src);
                    } else {
                        // Move to next number
                        @rename($src, $dst);
                    }
                }
            }
        }
    }
    
    /**
     * Send error response
     */
    private static function sendErrorResponse($statusCode, $message, $data = []) {
        if (!headers_sent()) {
            http_response_code($statusCode);
            
            if (self::$config['json_errors']) {
                header('Content-Type: application/json');
                echo json_encode([
                    'success' => false,
                    'error' => [
                        'code' => $statusCode,
                        'message' => $message,
                        'details' => $data
                    ]
                ], JSON_PRETTY_UNESCAPED_SLASHES);
            } else {
                // Fallback to HTML error page
                $errorPage = __DIR__ . "/../views/errors/{$statusCode}.php";
                if (file_exists($errorPage)) {
                    extract($data);
                    include $errorPage;
                } else {
                    echo "<h1>Error {$statusCode}</h1>";
                    echo "<p>{$message}</p>";
                    if (self::$config['display_errors'] && !empty($data)) {
                        echo "<pre>" . htmlspecialchars(print_r($data, true)) . "</pre>";
                    }
                }
            }
        }
        
        // Don't exit here to allow for cleanup in shutdown functions
    }
    
    /**
     * Get level name from level code
     */
    private static function getLevelName($level) {
        return self::$levels[$level] ?? 'UNKNOWN';
    }
    
    /**
     * Log a debug message
     */
    public static function debug($message, $context = []) {
        self::log(self::DEBUG, $message, $context);
    }
    
    /**
     * Log an info message
     */
    public static function info($message, $context = []) {
        self::log(self::INFO, $message, $context);
    }
    
    /**
     * Log a warning
     */
    public static function warning($message, $context = []) {
        self::log(self::WARNING, $message, $context);
    }
    
    /**
     * Log an error
     */
    public static function error($message, $context = []) {
        self::log(self::ERROR, $message, $context);
    }
    
    /**
     * Log a critical error
     */
    public static function critical($message, $context = []) {
        self::log(self::CRITICAL, $message, $context);
    }
}

// Initialize error handler with default settings
ErrorHandler::init([
    'display_errors' => ($_SERVER['SERVER_NAME'] ?? 'cli') === 'localhost' || PHP_SAPI === 'cli',
    'log_level' => ($_SERVER['SERVER_NAME'] ?? '') === 'production' ? ErrorHandler::WARNING : ErrorHandler::DEBUG
]);
