package egovframework.config;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Properties;

/**
 * MyBatis Interceptor를 이용한 SQL 로깅 및 Mapper 정보 추출
 * 
 * MappedStatement에서 정확한 namespace(Mapper 클래스)와 id(메서드명)를 추출하여
 * ThreadLocal을 통해 DataSourceProxy와 공유합니다.
 * 
 * @author XIT Framework
 */
@Intercepts({
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class SqlLoggingInterceptor implements Interceptor {
    
    private static final Logger logger = LoggerFactory.getLogger(SqlLoggingInterceptor.class);
    
    /**
     * ThreadLocal을 이용해 현재 실행 중인 Mapper 정보를 저장
     * DataSourceProxy에서 이 정보를 참조하여 정확한 Mapper 정보를 로그에 출력
     */
    private static final ThreadLocal<String> CURRENT_MAPPER_INFO = new ThreadLocal<>();
    
    /**
     * 현재 스레드의 Mapper 정보 반환
     * 
     * @return Mapper 클래스명.메서드명 (예: go.kr.project.login.mapper.LoginMapper.selectMenusByRoleIds)
     */
    public static String getCurrentMapperInfo() {
        String mapperInfo = CURRENT_MAPPER_INFO.get();
        return mapperInfo != null ? mapperInfo : "Unknown Mapper";
    }
    
    /**
     * 현재 스레드의 Mapper 정보 설정
     * 
     * @param mapperInfo Mapper 정보
     */
    public static void setCurrentMapperInfo(String mapperInfo) {
        CURRENT_MAPPER_INFO.set(mapperInfo);
    }
    
    /**
     * 현재 스레드의 Mapper 정보 제거
     */
    public static void clearCurrentMapperInfo() {
        CURRENT_MAPPER_INFO.remove();
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // MappedStatement에서 Mapper 정보 추출
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        String statementId = mappedStatement.getId();

        // statementId는 "namespace.methodId" 형태 (예: go.kr.project.login.mapper.LoginMapper.selectMenusByRoleIds)
        String mapperInfo = statementId;

        // ThreadLocal에 Mapper 정보 저장
        setCurrentMapperInfo(mapperInfo);

        logger.debug("MyBatis Interceptor - Mapper 정보 설정: {}", mapperInfo);

        try {
            // 실제 쿼리 실행
            return invocation.proceed();

        } catch (Exception e) {
            MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
            Object parameter = invocation.getArgs()[1];

            logger.error("MyBatis Interceptor 실행 중 오류 발생");
            logger.error("Query ID: {}", ms.getId());
            logger.error("Parameters: {}", parameter);
            logger.error("SQL: {}", ms.getBoundSql(parameter).getSql());
            logger.error("Error: {}", e.getMessage());
            // 중요로직: 예외 전체 스택트레이스를 가독성 있게 출력하기 위해 logger.error("message", exception) 형태를 사용
            logger.error("Error StackTrace: ", e);
            throw e;
        }
        // 주의: ThreadLocal 정리는 DataSourceProxy의 afterQuery에서 수행
        // finally 블록에서 정리하면 DataSourceProxy가 Mapper 정보를 사용하기 전에 제거될 수 있음
    }

    @Override
    public Object plugin(Object target) {
        // 모든 Executor에 대해 인터셉터 적용
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        // 설정 프로퍼티가 있다면 여기서 처리
        // 현재는 특별한 설정이 필요없음
    }
}