package ai.decompile.workspace.controller;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import ai.decompile.workspace.model.dto.WorkspaceRequest;
import ai.decompile.workspace.model.dto.WorkspaceResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.transaction.annotation.Transactional;

@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
@Transactional
class WorkspaceIntegrationTest {

  @Autowired private MockMvc mockMvc;

  private final ObjectMapper objectMapper = new ObjectMapper();

  @Test
  void getWorkspacesShouldReturnEmptyList() throws Exception {
    mockMvc
        .perform(get("/workspaces"))
        .andExpect(status().isOk())
        .andExpect(jsonPath("$").isArray())
        .andExpect(jsonPath("$").isEmpty());
  }

  @Test
  void getWorkspaceByIdShouldReturnWorkspace() throws Exception {
    var request = new WorkspaceRequest("Get By Id", "desc");
    var createdJson =
        mockMvc
            .perform(
                post("/workspaces")
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(objectMapper.writeValueAsString(request)))
            .andExpect(status().isOk())
            .andReturn()
            .getResponse()
            .getContentAsString();

    var created = objectMapper.readValue(createdJson, WorkspaceResponse.class);

    mockMvc
        .perform(get("/workspaces/{id}", created.id()))
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.id").value(created.id().toString()))
        .andExpect(jsonPath("$.name").value("Get By Id"))
        .andExpect(jsonPath("$.description").value("desc"))
        .andExpect(jsonPath("$.projectCount").value(0));
  }

  @Test
  void getWorkspacesShouldFilterByName() throws Exception {
    var requestA = new WorkspaceRequest("Alpha Project", "desc");
    var requestB = new WorkspaceRequest("Beta Test", "desc");

    mockMvc
        .perform(
            post("/workspaces")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(requestA)))
        .andExpect(status().isOk());

    mockMvc
        .perform(
            post("/workspaces")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(requestB)))
        .andExpect(status().isOk());

    mockMvc
        .perform(get("/workspaces").param("name", "alpha"))
        .andExpect(status().isOk())
        .andExpect(jsonPath("$").isArray())
        .andExpect(jsonPath("$.length()").value(1))
        .andExpect(jsonPath("$[0].name").value("Alpha Project"));
  }

  @Test
  void fullCrudFlow() throws Exception {
    var request = new WorkspaceRequest("CRUD Test", "CRUD Description");

    var createdJson =
        mockMvc
            .perform(
                post("/workspaces")
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(objectMapper.writeValueAsString(request)))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.name").value("CRUD Test"))
            .andReturn()
            .getResponse()
            .getContentAsString();

    var created = objectMapper.readValue(createdJson, WorkspaceResponse.class);

    var updateRequest = new WorkspaceRequest("CRUD Updated", "Updated Description");
    mockMvc
        .perform(
            put("/workspaces/{id}", created.id())
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(updateRequest)))
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.name").value("CRUD Updated"));

    mockMvc.perform(delete("/workspaces/{id}", created.id())).andExpect(status().isOk());

    mockMvc.perform(delete("/workspaces/{id}", created.id())).andExpect(status().isNotFound());
  }

  @Test
  void createWorkspaceShouldReturn409OnDuplicateName() throws Exception {
    var request = new WorkspaceRequest("Unique Name", "desc");

    mockMvc
        .perform(
            post("/workspaces")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(request)))
        .andExpect(status().isOk());

    mockMvc
        .perform(
            post("/workspaces")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(request)))
        .andExpect(status().isConflict())
        .andExpect(jsonPath("$.status").value(409))
        .andExpect(jsonPath("$.error").value("Conflict"));
  }

  @Test
  void nonexistentWorkspaceShouldReturn404() throws Exception {
    var nonExistentId = "00000000-0000-0000-0000-000000000000";
    var request = new WorkspaceRequest("Update", "desc");

    mockMvc
        .perform(get("/workspaces/{id}", nonExistentId))
        .andExpect(status().isNotFound())
        .andExpect(jsonPath("$.status").value(404));

    mockMvc
        .perform(
            put("/workspaces/{id}", nonExistentId)
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(request)))
        .andExpect(status().isNotFound())
        .andExpect(jsonPath("$.status").value(404));

    mockMvc
        .perform(delete("/workspaces/{id}", nonExistentId))
        .andExpect(status().isNotFound())
        .andExpect(jsonPath("$.status").value(404));
  }
}
