<?php
/**
 * سودانا — Logger v1
 * Centralized logging with trace_id support
 *
 * Replaces 145 scattered error_log() calls
 * Writes to: error_log + audit_logs table + execution_log (when applicable)
 */
final class Logger {
    private static ?string $traceId = null;
    private static array $context = [];

    /** Generate or reuse trace ID for current request */
    public static function traceId(): string {
        if (self::$traceId === null) {
            self::$traceId = bin2hex(random_bytes(8)); // 16 chars
        }
        return self::$traceId;
    }

    /** Set the trace ID explicitly (e.g. from incoming header X-Trace-Id) */
    public static function setTraceId(string $id): void {
        self::$traceId = preg_replace('/[^a-zA-Z0-9_-]/', '', substr($id, 0, 32));
    }

    /** Add context to be included in every log line */
    public static function setContext(string $key, $value): void {
        self::$context[$key] = $value;
    }

    /** Info level — normal operational messages */
    public static function info(string $message, array $extra = []): void {
        self::write('INFO', $message, $extra);
    }

    /** Warn level — recoverable issues */
    public static function warn(string $message, array $extra = []): void {
        self::write('WARN', $message, $extra);
    }

    /** Error level — failures that need attention */
    public static function error(string $message, array $extra = []): void {
        self::write('ERROR', $message, $extra);
    }

    /** Critical — system-threatening errors */
    public static function critical(string $message, array $extra = []): void {
        self::write('CRIT', $message, $extra);
        // Also try to persist to DB
        self::persistCritical($message, $extra);
    }

    private static function write(string $level, string $message, array $extra): void {
        $line = sprintf(
            '[%s] [%s] [%s] %s%s',
            date('Y-m-d H:i:s'),
            $level,
            self::traceId(),
            $message,
            $extra ? ' ' . json_encode(array_merge(self::$context, $extra), JSON_UNESCAPED_UNICODE) : ''
        );
        error_log($line);
    }

    private static function persistCritical(string $message, array $extra): void {
        try {
            db()->prepare(
                "INSERT INTO audit_logs (action, entity_type, details, ip_address, created_at)
                 VALUES ('CRITICAL_LOG', 'system', ?, ?, NOW())"
            )->execute([
                json_encode(['msg' => $message, 'trace' => self::traceId(),
                             'extra' => $extra], JSON_UNESCAPED_UNICODE),
                $_SERVER['REMOTE_ADDR'] ?? null,
            ]);
        } catch (Throwable $ignored) {}
    }

    /**
     * Log AI usage with trace_id linkage to ai_usage table
     * Called by UsageMeter and Gateway to ensure consistent recording
     */
    public static function aiUsage(int $userId, string $eventType, array $data): void {
        try {
            db()->prepare(
                "INSERT INTO ai_usage
                 (user_id, event_type, tokens_used, tokens_in, tokens_out,
                  cost_usd, cost_sdg, service, provider, trace_id, created_at)
                 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())"
            )->execute([
                $userId,
                $eventType,
                (int)($data['tokens_used'] ?? 0),
                (int)($data['tokens_in'] ?? 0),
                (int)($data['tokens_out'] ?? 0),
                (float)($data['cost_usd'] ?? 0),
                (int)($data['cost_sdg'] ?? 0),
                $data['service'] ?? 'general',
                $data['provider'] ?? null,
                self::traceId(),
            ]);
        } catch (Throwable $e) {
            self::warn("aiUsage log failed: " . $e->getMessage());
        }
    }

    /** Get all logs for a trace (for debugging) */
    public static function getTrace(string $traceId): array {
        try {
            $st = db()->prepare(
                "SELECT 'ai_usage' as src, created_at, event_type, service, tokens_used
                 FROM ai_usage WHERE trace_id=?
                 UNION ALL
                 SELECT 'audit_logs' as src, created_at, action, entity_type, NULL
                 FROM audit_logs WHERE details LIKE ?
                 ORDER BY created_at"
            );
            $st->execute([$traceId, '%' . $traceId . '%']);
            return $st->fetchAll() ?: [];
        } catch (Throwable $e) {
            return [];
        }
    }
}
