package com.caliverse.admin.global.common.aspect;

import com.caliverse.admin.global.common.code.CommonCode;
import com.caliverse.admin.global.common.code.ErrorCode;
import com.caliverse.admin.global.common.exception.RestApiException;
import com.caliverse.admin.global.component.transaction.DynamoDBTransactionContext;
import com.caliverse.admin.global.component.transaction.TransactionIdManager;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.enhanced.dynamodb.model.TransactWriteItemsEnhancedRequest;

import static com.caliverse.admin.global.common.constants.CommonConstants.TRANSACTION_ID_KEY;

@Aspect
@Component
@Slf4j
public class DynamoDBTransactionAspect {
    private final DynamoDbEnhancedClient enhancedClient;
    private final TransactionIdManager transactionIdManager;

    public DynamoDBTransactionAspect(DynamoDbEnhancedClient enhancedClient, TransactionIdManager transactionIdManager) {
        this.enhancedClient = enhancedClient;
        this.transactionIdManager = transactionIdManager;
    }

    @Around("@annotation(com.caliverse.admin.global.common.annotation.DynamoDBTransaction)")
    public Object executeInTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
        TransactWriteItemsEnhancedRequest.Builder transactionBuilder =
                TransactWriteItemsEnhancedRequest.builder();

        boolean hasExistingTransaction = TransactionSynchronizationManager.hasResource(TRANSACTION_ID_KEY);

        if (!hasExistingTransaction) {
            transactionIdManager.getCurrentTransactionId();
        }

        try {
            // 메서드 실행 전 트랜잭션 컨텍스트 설정
            DynamoDBTransactionContext.setTransactionBuilder(transactionBuilder);

            // 실제 메서드 실행
            Object result = joinPoint.proceed();

            // 트랜잭션 커밋
            TransactWriteItemsEnhancedRequest transactionRequest =
                    DynamoDBTransactionContext.getTransactionBuilder().build();
            if (transactionRequest.transactWriteItems() != null && !transactionRequest.transactWriteItems().isEmpty()) {
                enhancedClient.transactWriteItems(transactionRequest);
            }

            return result;
        } catch (DynamoDbException e) {
            log.error("DynamoDB transaction failed: {}", e.getMessage());
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(),
                    ErrorCode.DYNAMODB_PROCESS_ERROR.getMessage());
        } catch (Exception e) {
            log.error("DynamoDB operation error: {}", e.getMessage());
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(),
                    ErrorCode.DYNAMODB_CONNECTION_ERROR.getMessage());
        } finally {
            // 트랜잭션 컨텍스트 정리
            DynamoDBTransactionContext.clear();

            if (!hasExistingTransaction && TransactionSynchronizationManager.hasResource(TRANSACTION_ID_KEY)) {
                TransactionSynchronizationManager.unbindResource(TRANSACTION_ID_KEY);
            }
        }
    }
}
