<?php

namespace Mtc\Plugins\CmsAuditLog\Src;

use Illuminate\Support\Facades\Log;
use Mtc\Plugins\CmsAuditLog\Src\Models\AuditEntry;
use Throwable;

/**
 * Writes CMS change audit entries.
 *
 * Logging must never break a CMS save, so every write is wrapped
 * and failures are reported to the application log instead.
 */
class AuditLogger
{
    /**
     * Maximum length stored for a single old/new value in details.
     * Longer values are truncated - the page versioning system holds
     * the full historic content.
     */
    const MAX_VALUE_LENGTH = 5000;

    public static function log(string $action, ?int $pageId = null, ?string $pageTitle = null, array $details = []): void
    {
        if (!config('cms_audit_log.enabled')) {
            return;
        }

        try {
            [$adminUserId, $adminUserName] = self::resolveActor();

            AuditEntry::query()->create([
                'action' => $action,
                'page_id' => $pageId,
                'page_title' => $pageTitle !== null ? mb_substr($pageTitle, 0, 255) : null,
                'admin_user_id' => $adminUserId,
                'admin_user_name' => $adminUserName,
                'ip_address' => app()->runningInConsole() ? null : request()->ip(),
                // Substitute invalid UTF-8 (legacy content) rather than
                // letting json_encode return false and empty the details
                'details' => json_encode($details, JSON_INVALID_UTF8_SUBSTITUTE | JSON_PARTIAL_OUTPUT_ON_ERROR),
            ]);
        } catch (Throwable $exception) {
            Log::warning('CMS audit log write failed: ' . $exception->getMessage());
        }
    }

    /**
     * Truncate a content value so a single field cannot bloat the log.
     */
    public static function truncateValue(?string $value): ?string
    {
        if ($value === null || mb_strlen($value) <= self::MAX_VALUE_LENGTH) {
            return $value;
        }

        return mb_substr($value, 0, self::MAX_VALUE_LENGTH) . '... [truncated]';
    }

    /**
     * Resolve the acting admin user from the legacy admin session.
     *
     * @return array{0: int|null, 1: string}
     */
    protected static function resolveActor(): array
    {
        try {
            $adminUser = new \AdminUser();
            if (!empty($adminUser->user['id'])) {
                return [
                    (int) $adminUser->user['id'],
                    (string) ($adminUser->user['name'] ?? ''),
                ];
            }
        } catch (Throwable) {
            // Fall through to the content manager session below
        }

        if (!empty($_SESSION['content_manager']['user_id'])) {
            return [
                (int) $_SESSION['content_manager']['user_id'],
                (string) ($_SESSION['content_manager']['username'] ?? ''),
            ];
        }

        if (php_sapi_name() === 'cli') {
            return [null, 'System (CLI)'];
        }

        return [null, 'Unknown'];
    }
}
