package com.ga.api.support;

import com.ga.common.auth.JwtAuthFilter;
import com.ga.common.auth.JwtTokenProvider;
import com.ga.common.code.CommonCodeMapper;
import com.ga.common.file.FileMapper;
import com.ga.common.menu.MenuMapper;
import com.ga.common.notification.NotificationMapper;
import com.ga.common.system.ApiAccessLogService;
import com.ga.common.system.DataChangeLogService;
import com.ga.common.system.SystemConfigMapper;
import com.ga.common.system.SystemLogMapper;
import com.ga.core.mapper.ledger.ExceptionLedgerMapper;
import com.ga.core.mapper.ledger.MaintainLedgerMapper;
import com.ga.core.mapper.ledger.RecruitLedgerMapper;
import com.ga.core.mapper.org.AgentMapper;
import com.ga.core.mapper.org.GradeMapper;
import com.ga.core.mapper.org.OrganizationMapper;
import com.ga.core.mapper.product.ContractMapper;
import com.ga.core.mapper.product.InsuranceCompanyMapper;
import com.ga.core.mapper.product.ProductMapper;
import com.ga.core.mapper.receive.ReceiveMapper;
import com.ga.core.mapper.rule.RuleMapper;
import com.ga.core.mapper.settle.BatchJobLogMapper;
import com.ga.core.mapper.settle.ChargebackMapper;
import com.ga.core.mapper.settle.OverrideSettleMapper;
import com.ga.core.mapper.settle.PaymentMapper;
import com.ga.core.mapper.settle.ReconciliationMapper;
import com.ga.core.mapper.settle.SettleMasterMapper;
import com.ga.core.mapper.upload.UploadColumnMapper;
import com.ga.core.mapper.upload.UploadHistoryMapper;
import com.ga.core.mapper.upload.UploadTemplateMapper;
import com.ga.core.mapper.user.RoleMapper;
import com.ga.core.mapper.user.UserMapper;
import com.ga.api.service.upload.LookupMapper;
import com.ga.api.service.upload.DynamicInsertMapper;
import jakarta.servlet.FilterChain;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;

/**
 * Controller 테스트 베이스.
 *
 * @SpringBootTest + @AutoConfigureMockMvc 로 전체 Spring MVC 컨텍스트 사용.
 * application-test.yml 로 DB/Redis/Cache 자동설정 제외.
 * 모든 MyBatis Mapper 를 @MockBean 으로 등록하여 SqlSessionFactory 없이 동작.
 *
 * 각 테스트는:
 *   - 사용할 Service 를 @MockBean 으로 추가
 *   - PermissionChecker 를 @MockBean 으로 추가 후 hasPermission() stub
 *   - MockLoginUser.admin() 으로 인증된 요청 실행
 */
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@AutoConfigureMockMvc
@ActiveProfiles("test")
@Import({TestSecurityConfig.class, TestMyBatisConfig.class})
public abstract class AbstractControllerTest {

    @Autowired protected MockMvc mockMvc;

    // ---- 인증 ----
    @MockBean protected JwtAuthFilter jwtAuthFilter;
    @MockBean protected JwtTokenProvider jwtTokenProvider;

    /**
     * JwtAuthFilter 는 OncePerRequestFilter 를 상속한다.
     * @MockBean 으로 교체하면 doFilter() 가 chain.doFilter() 를 호출하지 않아
     * 요청이 DispatcherServlet 에 도달하지 못한다.
     * 이를 해결하기 위해 public doFilter() 를 pass-through 로 stub 한다.
     */
    @BeforeEach
    void stubJwtFilter() throws Exception {
        doAnswer(inv -> {
            jakarta.servlet.ServletRequest req = inv.getArgument(0);
            jakarta.servlet.ServletResponse res = inv.getArgument(1);
            FilterChain chain = inv.getArgument(2);
            chain.doFilter(req, res);
            return null;
        }).when(jwtAuthFilter).doFilter(
                any(jakarta.servlet.ServletRequest.class),
                any(jakarta.servlet.ServletResponse.class),
                any(FilterChain.class));
    }

    // ---- 로그 서비스 ----
    @MockBean protected DataChangeLogService dataChangeLogService;
    @MockBean protected ApiAccessLogService apiAccessLogService;

    // ---- 공통 Mapper ----
    @MockBean protected CommonCodeMapper commonCodeMapper;
    @MockBean protected FileMapper fileMapper;
    @MockBean protected MenuMapper menuMapper;
    @MockBean protected NotificationMapper notificationMapper;
    @MockBean protected SystemConfigMapper systemConfigMapper;
    @MockBean protected SystemLogMapper systemLogMapper;

    // ---- 도메인 Mapper ----
    @MockBean protected AgentMapper agentMapper;
    @MockBean protected GradeMapper gradeMapper;
    @MockBean protected OrganizationMapper organizationMapper;
    @MockBean protected ContractMapper contractMapper;
    @MockBean protected InsuranceCompanyMapper insuranceCompanyMapper;
    @MockBean protected ProductMapper productMapper;
    @MockBean protected RecruitLedgerMapper recruitLedgerMapper;
    @MockBean protected MaintainLedgerMapper maintainLedgerMapper;
    @MockBean protected ExceptionLedgerMapper exceptionLedgerMapper;
    @MockBean protected ReceiveMapper receiveMapper;
    @MockBean protected RuleMapper ruleMapper;
    @MockBean protected SettleMasterMapper settleMasterMapper;
    @MockBean protected PaymentMapper paymentMapper;
    @MockBean protected ChargebackMapper chargebackMapper;
    @MockBean protected OverrideSettleMapper overrideSettleMapper;
    @MockBean protected ReconciliationMapper reconciliationMapper;
    @MockBean protected BatchJobLogMapper batchJobLogMapper;
    @MockBean protected UploadHistoryMapper uploadHistoryMapper;
    @MockBean protected UploadTemplateMapper uploadTemplateMapper;
    @MockBean protected UploadColumnMapper uploadColumnMapper;
    @MockBean protected UserMapper userMapper;
    @MockBean protected RoleMapper roleMapper;

    // ---- ga-api 내부 Mapper ----
    @MockBean protected LookupMapper lookupMapper;
    @MockBean protected DynamicInsertMapper dynamicInsertMapper;
}
