package com.yimeng.okr.event;

import com.yimeng.okr.entity.Notification;
import com.yimeng.okr.mapper.NotificationMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataAccessException;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;

@Slf4j
@Component
@RequiredArgsConstructor
public class NotificationEventListener {

    private static final int MAX_INSERT_ATTEMPTS = 3;

    private final NotificationMapper notificationMapper;

    @Async
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, fallbackExecution = true)
    public void handleNotification(NotificationEvent event) {
        Notification notification = new Notification();
        notification.setUserId(event.getUserId());
        notification.setType(event.getType());
        notification.setTitle(event.getTitle());
        notification.setContent(event.getContent());
        notification.setRefType(event.getRefType());
        notification.setRefId(event.getRefId());
        notification.setIsRead(0);
        insertWithRetry(notification, event);
    }

    private void insertWithRetry(Notification notification, NotificationEvent event) {
        for (int attempt = 1; attempt <= MAX_INSERT_ATTEMPTS; attempt++) {
            try {
                notificationMapper.insert(notification);
                return;
            } catch (DataAccessException ex) {
                if (attempt == MAX_INSERT_ATTEMPTS) {
                    log.error("Failed to persist notification after {} attempts, userId={}, type={}, refType={}, refId={}",
                            MAX_INSERT_ATTEMPTS, event.getUserId(), event.getType(), event.getRefType(), event.getRefId(), ex);
                    throw ex;
                }
                sleepBeforeRetry(attempt);
            }
        }
    }

    private void sleepBeforeRetry(int attempt) {
        try {
            Thread.sleep(50L * attempt);
        } catch (InterruptedException interrupted) {
            Thread.currentThread().interrupt();
            throw new IllegalStateException("Notification retry interrupted", interrupted);
        }
    }
}
