package com.xly.erp.common.exception;

import com.xly.erp.common.response.Result;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

class GlobalExceptionHandlerTest {

    private MockMvc mockMvc;

    @BeforeEach
    void setUp() {
        mockMvc = MockMvcBuilders.standaloneSetup(new StubController())
                .setControllerAdvice(new GlobalExceptionHandler())
                .build();
    }

    @Test
    void bizException_returnsResultWithBizCode() throws Exception {
        mockMvc.perform(get("/__test/throw-biz"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(30001))
                .andExpect(jsonPath("$.msg").value("business x"));
    }

    @Test
    void validationException_returns40001WithFieldHint() throws Exception {
        mockMvc.perform(post("/__test/throw-validate")
                        .contentType("application/json")
                        .content("{}"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(40001))
                .andExpect(jsonPath("$.msg", org.hamcrest.Matchers.containsString("name")));
    }

    @Test
    void uncaughtException_returns50000() throws Exception {
        mockMvc.perform(get("/__test/throw-runtime"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(50000));
    }

    @Test
    void resultOkHelper_buildsZeroCode() {
        Result<String> r = Result.ok("hi");
        assertThat(r.getCode()).isZero();
        assertThat(r.getData()).isEqualTo("hi");
    }

    @RestController
    static class StubController {
        @GetMapping("/__test/throw-biz")
        public Result<Void> throwBiz() {
            throw new BizException(30001, "business x");
        }

        @PostMapping("/__test/throw-validate")
        public Result<Void> throwValidate(@Valid @RequestBody StubBody body) {
            return Result.ok();
        }

        @GetMapping("/__test/throw-runtime")
        public Result<Void> throwRuntime() {
            throw new IllegalStateException("boom");
        }
    }

    static class StubBody {
        @NotBlank
        public String name;
    }
}
