package de.szut.casino.user;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
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.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;

import com.fasterxml.jackson.databind.ObjectMapper;

import de.szut.casino.user.dto.CreateUserDto;
import de.szut.casino.user.dto.GetUserDto;

@WebMvcTest(UserController.class)
@AutoConfigureMockMvc(addFilters = false)
public class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @MockBean
    private UserService userService;

    private GetUserDto getUserDto;
    private CreateUserDto createUserDto;
    private UserEntity testUser;
    private final String TEST_ID = "test-id-123";
    private final String AUTH_TOKEN = "Bearer test-token";

    @BeforeEach
    void setUp() {
        getUserDto = new GetUserDto();
        getUserDto.setAuthentikId(TEST_ID);
        getUserDto.setUsername("testuser");

        testUser = new UserEntity();
        testUser.setAuthentikId(TEST_ID);
        testUser.setUsername("testuser");

        createUserDto = new CreateUserDto();
        createUserDto.setAuthentikId(TEST_ID);
        createUserDto.setUsername("testuser");
    }

    @Test
    void getUserByIdSuccess() throws Exception {
        when(userService.exists(TEST_ID)).thenReturn(true);
        when(userService.getUser(TEST_ID)).thenReturn(getUserDto);

        mockMvc.perform(get("/user/" + TEST_ID))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.authentikId").value(TEST_ID))
                .andExpect(jsonPath("$.username").value("testuser"));
    }

    @Test
    void getUserByIdNotFound() throws Exception {
        when(userService.exists(TEST_ID)).thenReturn(false);

        mockMvc.perform(get("/user/" + TEST_ID))
                .andExpect(status().isNotFound());
    }

    @Test
    void createUserSuccess() throws Exception {
        when(userService.exists(TEST_ID)).thenReturn(false);
        when(userService.createUser(any(CreateUserDto.class))).thenReturn(testUser);

        mockMvc.perform(post("/user")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(createUserDto)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.authentikId").value(TEST_ID))
                .andExpect(jsonPath("$.username").value("testuser"));
    }

    @Test
    void createUserAlreadyExists() throws Exception {
        when(userService.exists(TEST_ID)).thenReturn(true);

        mockMvc.perform(post("/user")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(createUserDto)))
                .andExpect(status().isFound())
                .andExpect(header().string("Location", "/user/" + TEST_ID));
    }

    @Test
    void getCurrentUserSuccess() throws Exception {
        when(userService.getCurrentUser(AUTH_TOKEN)).thenReturn(getUserDto);

        mockMvc.perform(get("/user")
                        .header("Authorization", AUTH_TOKEN))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.authentikId").value(TEST_ID))
                .andExpect(jsonPath("$.username").value("testuser"));
    }

    @Test
    void getCurrentUserNotFound() throws Exception {
        when(userService.getCurrentUser(anyString())).thenReturn(null);

        mockMvc.perform(get("/user")
                        .header("Authorization", AUTH_TOKEN))
                .andExpect(status().isNotFound());
    }
}