<?php

declare(strict_types=1);

namespace App\Services;

use App\Support\DberpAuditCorrelationContext;
use App\Support\DberpAuditTrailGuard;
use DBCore\Audit\AuditManager;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Auth;

/**
 * DBErp 宿主侧审计写入：复用 DBCore AuditManager → core_audit_logs。
 */
final class DberpAuditService
{
    public function __construct(
        private readonly AuditManager $auditManager,
    ) {}

    /**
     * @param  array<string, mixed>|null  $oldValues
     * @param  array<string, mixed>|null  $newValues
     */
    public function log(string $action, ?Model $auditable = null, ?array $oldValues = null, ?array $newValues = null): void
    {
        if (! config('dberp.audit.use_dbcore', true) || ! config('dbcore.modules.audit', true)) {
            return;
        }

        $this->auditManager->log($action, [
            'user_id' => Auth::id(),
            'auditable_type' => $auditable ? $auditable->getMorphClass() : null,
            'auditable_id' => $auditable?->getKey(),
            'old_values' => $oldValues,
            'new_values' => $newValues,
            'ip_address' => request()?->ip(),
            'user_agent' => request()?->userAgent(),
            'correlation_id' => DberpAuditCorrelationContext::id(),
        ]);
    }

    /**
     * 抑制模型 trait 自动审计后执行 save，并写入语义化业务审计。
     *
     * @param  callable(): void  $save
     * @param  array<string, mixed>|null  $oldValues
     * @param  array<string, mixed>|null  $newValues
     */
    public function logSemanticSave(string $action, ?Model $auditable, callable $save, ?array $oldValues = null, ?array $newValues = null): void
    {
        DberpAuditTrailGuard::withoutAuditTrail($save);
        $this->log($action, $auditable, $oldValues, $newValues);
    }

    /**
     * @param  array<string, mixed>  $only
     */
    public function snapshot(Model $model, array $only): array
    {
        return array_intersect_key($model->getAttributes(), array_flip($only));
    }

    /**
     * @param  array<string, mixed>  $attributes
     * @param  array<string, mixed>  $only
     */
    public function snapshotFromArray(array $attributes, array $only): array
    {
        return array_intersect_key($attributes, array_flip($only));
    }
}
