package org.raddatz.familienarchiv.audit;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.util.Map;
import java.util.UUID;

@Service
@RequiredArgsConstructor
@Slf4j
public class AuditService {

    private final AuditLogRepository auditLogRepository;
    @Qualifier("auditExecutor")
    private final TaskExecutor auditExecutor;

    @Async("auditExecutor")
    public void log(AuditKind kind, UUID actorId, UUID documentId, Map<String, Object> payload) {
        writeLog(kind, actorId, documentId, payload);
    }

    public void logAfterCommit(AuditKind kind, UUID actorId, UUID documentId, Map<String, Object> payload) {
        if (TransactionSynchronizationManager.isActualTransactionActive()) {
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCommit() {
                    // Run on a separate thread: the afterCommit() callback fires while Spring's
                    // transaction synchronizations are still active on the current thread, which
                    // prevents SimpleJpaRepository.save() from starting a new transaction inline.
                    auditExecutor.execute(() -> writeLog(kind, actorId, documentId, payload));
                }
            });
        } else {
            writeLog(kind, actorId, documentId, payload);
        }
    }

    private void writeLog(AuditKind kind, UUID actorId, UUID documentId, Map<String, Object> payload) {
        try {
            auditLogRepository.save(AuditLog.builder()
                    .kind(kind)
                    .actorId(actorId)
                    .documentId(documentId)
                    .payload(payload)
                    .build());
        } catch (Exception e) {
            log.error("Audit log write failed: kind={}, document={}", kind, documentId, e);
        }
    }
}
