package ai.decompile.workspace.service;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

import ai.decompile.common.exception.ConflictException;
import ai.decompile.common.exception.NotFoundException;
import ai.decompile.workspace.model.dto.WorkspaceRequest;
import ai.decompile.workspace.model.dto.WorkspaceResponse;
import ai.decompile.workspace.model.entity.Workspace;
import ai.decompile.workspace.model.repository.WorkspaceRepository;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.data.jpa.domain.Specification;

@ExtendWith(MockitoExtension.class)
class WorkspaceServiceTest {

  @Mock private WorkspaceRepository workspaceRepository;

  @InjectMocks private WorkspaceService workspaceService;

  private UUID id;
  private Workspace workspace;
  private WorkspaceRequest request;

  @BeforeEach
  void setUp() {
    id = UUID.randomUUID();
    workspace =
        Workspace.builder()
            .id(id)
            .name("Test Workspace")
            .description("Test Description")
            .createdAt(Instant.now())
            .updatedAt(Instant.now())
            .projects(List.of())
            .build();
    request = new WorkspaceRequest("Test Workspace", "Test Description");
  }

  @Test
  void getWorkspacesShouldReturnEmptyList() {
    when(workspaceRepository.findAll()).thenReturn(List.of());

    List<WorkspaceResponse> result = workspaceService.getWorkspaces(null);

    assertTrue(result.isEmpty());
    verify(workspaceRepository).findAll();
  }

  @Test
  void getWorkspacesShouldReturnList() {
    when(workspaceRepository.findAll()).thenReturn(List.of(workspace));

    List<WorkspaceResponse> result = workspaceService.getWorkspaces(null);

    assertEquals(1, result.size());
    assertEquals(id, result.getFirst().id());
    assertEquals("Test Workspace", result.getFirst().name());
    assertEquals(0, result.getFirst().projectCount());
  }

  @Test
  void getWorkspacesShouldFilterByName() {
    when(workspaceRepository.findAll(any(Specification.class))).thenReturn(List.of(workspace));

    List<WorkspaceResponse> result = workspaceService.getWorkspaces("test");

    assertEquals(1, result.size());
    assertEquals("Test Workspace", result.getFirst().name());
    assertEquals(0, result.getFirst().projectCount());
    verify(workspaceRepository).findAll(any(Specification.class));
  }

  @Test
  void getWorkspaceByIdShouldReturnWorkspace() {
    when(workspaceRepository.findById(id)).thenReturn(Optional.of(workspace));

    WorkspaceResponse result = workspaceService.getWorkspaceById(id);

    assertEquals(id, result.id());
    assertEquals("Test Workspace", result.name());
    assertEquals(0, result.projectCount());
    verify(workspaceRepository).findById(id);
  }

  @Test
  void getWorkspaceByIdShouldThrowNotFound() {
    when(workspaceRepository.findById(id)).thenReturn(Optional.empty());

    NotFoundException ex =
        assertThrows(NotFoundException.class, () -> workspaceService.getWorkspaceById(id));

    assertTrue(ex.getMessage().contains(id.toString()));
  }

  @Test
  void createWorkspaceShouldSucceed() {
    when(workspaceRepository.existsByNameIgnoreCase(request.name())).thenReturn(false);
    when(workspaceRepository.save(any(Workspace.class))).thenReturn(workspace);

    WorkspaceResponse result = workspaceService.createWorkspace(request);

    assertEquals(id, result.id());
    assertEquals("Test Workspace", result.name());
    verify(workspaceRepository).existsByNameIgnoreCase(request.name());
    verify(workspaceRepository).save(any(Workspace.class));
  }

  @Test
  void createWorkspaceShouldThrowConflictWhenNameExists() {
    when(workspaceRepository.existsByNameIgnoreCase(request.name())).thenReturn(true);

    ConflictException ex =
        assertThrows(ConflictException.class, () -> workspaceService.createWorkspace(request));

    assertTrue(ex.getMessage().contains("Test Workspace"));
    verify(workspaceRepository, never()).save(any());
  }

  @Test
  void updateWorkspaceShouldSucceed() {
    when(workspaceRepository.findById(id)).thenReturn(Optional.of(workspace));
    when(workspaceRepository.save(any(Workspace.class))).thenReturn(workspace);

    WorkspaceResponse result = workspaceService.updateWorkspace(id, request);

    assertEquals(id, result.id());
    verify(workspaceRepository).findById(id);
    verify(workspaceRepository).save(any(Workspace.class));
  }

  @Test
  void updateWorkspaceShouldThrowNotFound() {
    when(workspaceRepository.findById(id)).thenReturn(Optional.empty());

    NotFoundException ex =
        assertThrows(NotFoundException.class, () -> workspaceService.updateWorkspace(id, request));

    assertTrue(ex.getMessage().contains(id.toString()));
    verify(workspaceRepository, never()).save(any());
  }

  @Test
  void deleteWorkspaceShouldSucceed() {
    when(workspaceRepository.findById(id)).thenReturn(Optional.of(workspace));

    workspaceService.deleteWorkspace(id);

    verify(workspaceRepository).findById(id);
    verify(workspaceRepository).delete(workspace);
  }

  @Test
  void deleteWorkspaceShouldThrowNotFound() {
    when(workspaceRepository.findById(id)).thenReturn(Optional.empty());

    NotFoundException ex =
        assertThrows(NotFoundException.class, () -> workspaceService.deleteWorkspace(id));

    assertTrue(ex.getMessage().contains(id.toString()));
    verify(workspaceRepository, never()).delete(any(Workspace.class));
  }
}
