package at.procon.eventhub.tachographfilesession.api;

import static org.mockito.ArgumentMatchers.eq;
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.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import at.procon.eventhub.dto.DriverCardRefDto;
import at.procon.eventhub.dto.DriverRefDto;
import at.procon.eventhub.dto.EventDomain;
import at.procon.eventhub.dto.EventHubEventDto;
import at.procon.eventhub.dto.EventLifecycle;
import at.procon.eventhub.dto.EventType;
import at.procon.eventhub.dto.VehicleRefDto;
import at.procon.eventhub.dto.VehicleRegistrationRefDto;
import at.procon.eventhub.tachographfilesession.dto.CreateTachographCompositeSessionResponse;
import at.procon.eventhub.tachographfilesession.dto.TachographCompositeDriverEventsResponse;
import at.procon.eventhub.tachographfilesession.dto.TachographCompositeSessionListDriversResponse;
import at.procon.eventhub.tachographfilesession.dto.TachographCompositeSessionSummaryDto;
import at.procon.eventhub.tachographfilesession.dto.TachographFileDriverSummaryDto;
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
import at.procon.eventhub.tachographfilesession.service.DriverNotFoundInCompositeSessionException;
import at.procon.eventhub.tachographfilesession.service.TachographCompositeSessionService;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.UUID;
import org.junit.jupiter.api.Test;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

class TachographCompositeSessionControllerTest {

    @Test
    void createsLoadsListsAndProcessesCompositeSession() throws Exception {
        TachographCompositeSessionService service = org.mockito.Mockito.mock(TachographCompositeSessionService.class);
        MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new TachographCompositeSessionController(service))
                .setControllerAdvice(new TachographFileSessionExceptionHandler())
                .build();

        UUID compositeSessionId = UUID.randomUUID();
        UUID firstSessionId = UUID.randomUUID();
        UUID secondSessionId = UUID.randomUUID();
        TachographFileDriverSummaryDto driver = new TachographFileDriverSummaryDto("12:123", "Muster", "Max", "12", "CARD0000000001", 5, 3);
        TachographCompositeSessionSummaryDto summary = new TachographCompositeSessionSummaryDto(
                compositeSessionId,
                "default",
                "fleet-week-1",
                List.of(firstSessionId, secondSessionId),
                List.of(driver),
                Instant.parse("2026-05-22T10:00:00Z")
        );
        when(service.createCompositeSession(org.mockito.ArgumentMatchers.any()))
                .thenReturn(new CreateTachographCompositeSessionResponse(summary));
        when(service.getCompositeSession(compositeSessionId)).thenReturn(summary);
        when(service.listDrivers(compositeSessionId))
                .thenReturn(new TachographCompositeSessionListDriversResponse(compositeSessionId, List.of(driver)));
        when(service.getMergedDriverEvents(compositeSessionId, "12:123"))
                .thenReturn(new TachographCompositeDriverEventsResponse(
                        compositeSessionId,
                        "12:123",
                        List.of(firstSessionId, secondSessionId),
                        1,
                        List.of(event("EVT-1", OffsetDateTime.parse("2026-05-22T08:00:00Z"), EventType.WORK))
                ));
        when(service.getMergedDriverTimeline(compositeSessionId, "12:123"))
                .thenReturn(new ResolvedDriverTimeline(
                        "COMPOSITE_TACHOGRAPH_FILE_SESSION",
                        OffsetDateTime.parse("2026-05-22T08:00:00Z"),
                        OffsetDateTime.parse("2026-05-22T10:00:00Z"),
                        List.of(),
                        List.of(),
                        List.of(),
                        List.of()
                ));

        mockMvc.perform(post("/api/eventhub/tachograph-composite-sessions")
                        .contentType("application/json")
                        .content("""
                                {
                                  "sessionIds": ["%s", "%s"],
                                  "label": "fleet-week-1"
                                }
                                """.formatted(firstSessionId, secondSessionId)))
                .andExpect(status().isCreated())
                .andExpect(jsonPath("$.session.compositeSessionId").value(compositeSessionId.toString()))
                .andExpect(jsonPath("$.session.memberSessionIds[0]").value(firstSessionId.toString()));

        mockMvc.perform(get("/api/eventhub/tachograph-composite-sessions/{compositeSessionId}", compositeSessionId))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.compositeSessionId").value(compositeSessionId.toString()))
                .andExpect(jsonPath("$.drivers[0].driverKey").value("12:123"));

        mockMvc.perform(get("/api/eventhub/tachograph-composite-sessions/{compositeSessionId}/drivers", compositeSessionId))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.drivers[0].cardVehicleUsageIntervalCount").value(3));

        mockMvc.perform(get("/api/eventhub/tachograph-composite-sessions/{compositeSessionId}/drivers/{driverKey}/events",
                        compositeSessionId, "12:123"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.eventCount").value(1))
                .andExpect(jsonPath("$.events[0].externalSourceEventId").value("EVT-1"))
                .andExpect(jsonPath("$.events[0].occurredAt").value("2026-05-22T08:00:00Z"));

        mockMvc.perform(get("/api/eventhub/tachograph-composite-sessions/{compositeSessionId}/drivers/{driverKey}/timeline",
                        compositeSessionId, "12:123"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.sourceKind").value("COMPOSITE_TACHOGRAPH_FILE_SESSION"))
                .andExpect(jsonPath("$.loadedFrom").value("2026-05-22T08:00:00Z"));
    }

    @Test
    void returnsNotFoundWhenDriverIsMissingInCompositeSession() throws Exception {
        TachographCompositeSessionService service = org.mockito.Mockito.mock(TachographCompositeSessionService.class);
        MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new TachographCompositeSessionController(service))
                .setControllerAdvice(new TachographFileSessionExceptionHandler())
                .build();

        UUID compositeSessionId = UUID.randomUUID();
        when(service.getMergedDriverEvents(eq(compositeSessionId), eq("missing")))
                .thenThrow(new DriverNotFoundInCompositeSessionException(compositeSessionId, "missing"));

        mockMvc.perform(get("/api/eventhub/tachograph-composite-sessions/{compositeSessionId}/drivers/{driverKey}/events",
                        compositeSessionId, "missing"))
                .andExpect(status().isNotFound())
                .andExpect(jsonPath("$.message").value(
                        "Driver 'missing' was not found in tachograph composite session '%s'.".formatted(compositeSessionId)
                ));
    }

    private static EventHubEventDto event(String externalSourceEventId, OffsetDateTime occurredAt, EventType eventType) {
        return new EventHubEventDto(
                null,
                externalSourceEventId,
                new DriverRefDto(null, new DriverCardRefDto("12", 12, "CARD0000000001")),
                new VehicleRefDto(null, "VIN00000000000001", null, new VehicleRegistrationRefDto("12", 12, "W-12345A")),
                occurredAt,
                null,
                null,
                EventDomain.DRIVER_ACTIVITY,
                eventType,
                EventLifecycle.START,
                null,
                null,
                null,
                null,
                null,
                false,
                null
        );
    }
}
