package com.xly.erp.common.security;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.FilterChain;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

class JwtAuthenticationFilterTest {

    private static final String SECRET = "f8d4be76bff13bf32fa33ca0b14a4b152ad01ca5719f57df18ec4ecf2370b235";

    private JwtUtil jwtUtil;
    private JwtAuthenticationFilter filter;

    @BeforeEach
    void setUp() {
        jwtUtil = new JwtUtil(SECRET);
        filter = new JwtAuthenticationFilter(jwtUtil, new ObjectMapper());
    }

    @AfterEach
    void clearContext() {
        SecurityContextHolder.clearContext();
    }

    @Test
    void validJwt_setsPrincipalInSecurityContext_andCallsChain() throws Exception {
        String token = jwtUtil.sign("USER001");
        MockHttpServletRequest req = new MockHttpServletRequest("POST", "/api/anything");
        req.addHeader("Authorization", "Bearer " + token);
        MockHttpServletResponse resp = new MockHttpServletResponse();
        FilterChain chain = mock(FilterChain.class);

        filter.doFilter(req, resp, chain);

        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        assertThat(auth).isNotNull();
        assertThat(auth.getPrincipal()).isEqualTo("USER001");
        verify(chain, times(1)).doFilter(req, resp);
    }

    @Test
    void tamperedJwt_writesJsonResultWith20001_andDoesNotCallChain() throws Exception {
        MockHttpServletRequest req = new MockHttpServletRequest("POST", "/api/anything");
        req.addHeader("Authorization", "Bearer not.a.real.jwt");
        MockHttpServletResponse resp = new MockHttpServletResponse();
        FilterChain chain = mock(FilterChain.class);

        filter.doFilter(req, resp, chain);

        verify(chain, never()).doFilter(req, resp);
        assertThat(resp.getContentType()).contains("application/json");
        JsonNode body = new ObjectMapper().readTree(resp.getContentAsString());
        assertThat(body.get("code").asInt()).isEqualTo(20001);
    }

    @Test
    void noJwtHeader_passesChainThrough_withoutSettingContext() throws Exception {
        MockHttpServletRequest req = new MockHttpServletRequest("POST", "/api/anything");
        MockHttpServletResponse resp = new MockHttpServletResponse();
        FilterChain chain = mock(FilterChain.class);

        filter.doFilter(req, resp, chain);

        assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();
        verify(chain, times(1)).doFilter(req, resp);
    }
}
