package org.dromara.common.web.handler;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
import cn.hutool.extra.spring.SpringUtil;
import jakarta.servlet.http.HttpServletRequest;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.exception.base.BaseException;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.context.support.StaticMessageSource;

import java.util.Locale;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
@Tag("dev")
class GlobalExceptionHandlerTest {

    @Mock
    private HttpServletRequest request;

    private GlobalExceptionHandler handler;
    private GenericApplicationContext applicationContext;

    @BeforeEach
    void setUp() {
        StaticMessageSource messageSource = new StaticMessageSource();
        messageSource.addMessage("operation.fail", Locale.getDefault(), "操作失败");
        applicationContext = new GenericApplicationContext();
        applicationContext.registerBean("messageSource", StaticMessageSource.class, () -> messageSource);
        applicationContext.refresh();
        new SpringUtil().setApplicationContext(applicationContext);
        handler = new GlobalExceptionHandler();
        when(request.getRequestURI()).thenReturn("/app/v1/device/D01");
    }

    @AfterEach
    void tearDown() {
        applicationContext.close();
    }

    @Test
    void handleRuntimeException_hidesInternalMessage() {
        R<Void> result = handler.handleRuntimeException(
            new IllegalStateException("jdbc:mysql://internal-host/water"), request);

        assertThat(result.getCode()).isEqualTo(R.FAIL);
        assertThat(result.getMsg()).doesNotContain("internal-host");
    }

    @Test
    void handleException_hidesInternalMessage() {
        R<Void> result = handler.handleException(
            new Exception("oss accessKeySecret=internal-secret"), request);

        assertThat(result.getCode()).isEqualTo(R.FAIL);
        assertThat(result.getMsg()).doesNotContain("internal-secret");
    }

    @Test
    void handleBaseException_logsThrowableAndRequestUri() {
        Logger logger = (Logger) LoggerFactory.getLogger(GlobalExceptionHandler.class);
        ListAppender<ILoggingEvent> appender = new ListAppender<>();
        appender.start();
        logger.addAppender(appender);
        try {
            handler.handleBaseException(new BaseException("user", "业务校验失败"), request);

            ILoggingEvent event = appender.list.get(appender.list.size() - 1);
            assertThat(event.getLevel()).isEqualTo(Level.WARN);
            assertThat(event.getFormattedMessage()).contains("/app/v1/device/D01");
            assertThat(event.getThrowableProxy()).isNotNull();
        } finally {
            logger.detachAppender(appender);
        }
    }
}
