package incheon.com.config.aspect;

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 java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Aspect
@Component
@Slf4j
public class ServiceAspect {

    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Around("execution(* incheon.ags.ias..service.impl.*ServiceImpl.*(..))")
    public Object aroundAllServiceMethods(ProceedingJoinPoint joinPoint) throws Throwable {

        String methodName = joinPoint.getSignature().getName();
        String crudType = determineCrudType(methodName);

        return collectCrudStatistics(joinPoint, crudType);
    }

    private String determineCrudType(String methodName) {
        String lowerMethodName = methodName.toLowerCase();

        // SELECT 타입 판별
        if (lowerMethodName.startsWith("select") ||
                lowerMethodName.startsWith("get") ||
                lowerMethodName.startsWith("find") ||
                lowerMethodName.startsWith("list") ||
                lowerMethodName.startsWith("search") ||
                lowerMethodName.startsWith("count") ||
                lowerMethodName.contains("list")) {
            return "SELECT";
        }

        // INSERT 타입 판별
        if (lowerMethodName.startsWith("insert") ||
                lowerMethodName.startsWith("add") ||
                lowerMethodName.startsWith("create") ||
                lowerMethodName.startsWith("save") ||
                lowerMethodName.startsWith("register")) {
            return "INSERT";
        }

        // UPDATE 타입 판별
        if (lowerMethodName.startsWith("update") ||
                lowerMethodName.startsWith("modify") ||
                lowerMethodName.startsWith("edit") ||
                lowerMethodName.startsWith("change")) {
            return "UPDATE";
        }

        // DELETE 타입 판별
        if (lowerMethodName.startsWith("delete") ||
                lowerMethodName.startsWith("remove") ||
                lowerMethodName.startsWith("drop")) {
            return "DELETE";
        }

        // 판별 불가능한 경우
        return "UNKNOWN";
    }

    private Object collectCrudStatistics(ProceedingJoinPoint joinPoint, String crudType) throws Throwable {

        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        String currentTime = LocalDateTime.now().format(formatter);
        long startTime = System.currentTimeMillis();

        // 요청 로그
        log.info("CRUD_START|{}|{}|{}|{}|{}",
                crudType, className, methodName, currentTime, getParameterCount(joinPoint));

        try {
            Object result = joinPoint.proceed();
            long endTime = System.currentTimeMillis();
            long executionTime = endTime - startTime;

            log.info("CRUD_SUCCESS|{}|{}|{}|{}|{}ms|{}",
                    crudType, className, methodName, currentTime, executionTime, getResultInfo(result, crudType));
            return result;

        } catch (Exception e) {
            long endTime = System.currentTimeMillis();
            long executionTime = endTime - startTime;

            // 실패 통계 로깅
            log.error("CRUD_FAILED|{}|{}|{}|{}|{}ms|{}",
                    crudType, className, methodName, currentTime, executionTime, e.getClass().getSimpleName());

            throw e;
        }
    }

    private int getParameterCount(ProceedingJoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        return args != null ? args.length : 0;
    }

    private String getResultInfo(Object result, String crudType) {
        if (result == null) {
            return "NULL";
        } else if (result instanceof java.util.List) {
            return "LIST_SIZE:" + ((java.util.List<?>) result).size();
        } else if (result instanceof Integer) {
            // SELECT 작업일 때는 COUNT로, 나머지는 AFFECTED_ROWS로
            if ("SELECT".equals(crudType)) {
                return "COUNT:" + result;
            } else {
                return "AFFECTED_ROWS:" + result;
            }
        } else if (result instanceof Long) {
            if ("SELECT".equals(crudType)) {
                return "COUNT:" + result;
            } else {
                return "AFFECTED_ROWS:" + result;
            }
        } else {
            return "OBJECT:" + result.getClass().getSimpleName();
        }
    }
}
