package at.procon.eventhub.tachographfilesession.service;

import static org.assertj.core.api.Assertions.assertThat;

import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession;
import at.procon.eventhub.tachographfilesession.model.ExtractedCardActivityInterval;
import at.procon.eventhub.tachographfilesession.model.ExtractedCardVehicleUsageInterval;
import at.procon.eventhub.tachographfilesession.model.ExtractionWarning;
import at.procon.eventhub.tachographfilesession.model.ExtractedSupportEvent;
import at.procon.eventhub.tachographfilesession.model.ExtractionStats;
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
import at.procon.eventhub.tachographfilesession.model.TachographEsperActivityIntervalEvent;
import at.procon.eventhub.tachographfilesession.model.TachographEsperVuCardAbsentIntervalEvent;
import at.procon.eventhub.tachographfilesession.model.TachographEsperVehicleUsageIntervalEvent;
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
import at.procon.eventhub.tachographfilesession.model.TachographFileSessionMetadata;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.junit.jupiter.api.Test;

class DriverTimelineBuilderTest {

    private final DriverTimelineBuilder builder = new DriverTimelineBuilder();

    @Test
    void mergesTouchingVehicleUsageIntervalsAndNormalizesUnknownActivity() {
        DriverExtractionSession driver = new DriverExtractionSession(
                "12:123",
                null,
                null,
                List.of(),
                List.of(),
                List.of(
                        new ExtractedCardVehicleUsageInterval(
                                "CVU-1",
                                OffsetDateTime.parse("2026-05-01T00:00:00Z"),
                                OffsetDateTime.parse("2026-05-01T23:59:59Z"),
                                100L,
                                200L,
                                "12:REG-1",
                                "VIN-1",
                                "a"
                        ),
                        new ExtractedCardVehicleUsageInterval(
                                "CVU-2",
                                OffsetDateTime.parse("2026-05-02T00:00:00Z"),
                                OffsetDateTime.parse("2026-05-02T08:00:00Z"),
                                201L,
                                250L,
                                "12:REG-1",
                                "VIN-1",
                                "b"
                        )
                ),
                List.of(
                        new ExtractedCardActivityInterval(
                                "ACT-1",
                                OffsetDateTime.parse("2026-05-01T08:00:00Z"),
                                OffsetDateTime.parse("2026-05-01T09:00:00Z"),
                                "UNKNOWN_ACTIVITY",
                                "DRIVER",
                                "INSERTED",
                                "SINGLE",
                                "12:REG-1",
                                "VIN-1",
                                "c"
                        )
                ),
                List.of(new ExtractedSupportEvent(
                        "SUP-1",
                        OffsetDateTime.parse("2026-05-01T08:30:00Z"),
                        "PLACE",
                        "BEGIN_DAILY_WORK_PERIOD",
                        "DRIVER",
                        "12:REG-1",
                        "VIN-1",
                        null,
                        null,
                        null,
                        null,
                        null,
                        null,
                        null,
                        "d"
                )),
                List.of(new ExtractionWarning("W1", "warning", "/x"))
        );
        TachographFileSession session = new TachographFileSession(
                UUID.randomUUID(),
                new TachographFileSessionMetadata("default", "legalrequirements-drivercard", "sample", "sample.ddd", "a", 3, "42", "b", true, null),
                Map.of(driver.driverKey(), driver),
                new ExtractionStats(1, 1, 2, 1, 1, 1),
                List.of(new ExtractionWarning("W2", "session warning", "/y")),
                Instant.now(),
                Instant.now().plus(4, ChronoUnit.HOURS)
        );

        ResolvedDriverTimeline timeline = builder.build(session, driver);

        assertThat(timeline.sourceKind()).isEqualTo("DRIVER_CARD");
        assertThat(timeline.vehicleUsageIntervals()).hasSize(1);
        assertThat(timeline.vehicleUsageIntervals().get(0).sessionId()).isEqualTo(session.sessionId());
        assertThat(timeline.vehicleUsageIntervals().get(0).driverKey()).isEqualTo("12:123");
        assertThat(timeline.vehicleUsageIntervals().get(0).from()).isEqualTo(OffsetDateTime.parse("2026-05-01T00:00:00Z"));
        assertThat(timeline.vehicleUsageIntervals().get(0).to()).isEqualTo(OffsetDateTime.parse("2026-05-02T08:00:00Z"));
        assertThat(timeline.activityIntervals()).hasSize(1);
        assertThat(timeline.activityIntervals().get(0).activityType()).isEqualTo("UNKNOWN");
        assertThat(timeline.loadedFrom()).isEqualTo(OffsetDateTime.parse("2026-05-01T00:00:00Z"));
        assertThat(timeline.loadedTo()).isEqualTo(OffsetDateTime.parse("2026-05-02T08:00:00Z"));
        assertThat(timeline.warnings()).extracting(ExtractionWarning::code).containsExactly("W2", "W1");
    }

    @Test
    void mergesTouchingOpenEndedVehicleUsageInterval() {
        DriverExtractionSession driver = new DriverExtractionSession(
                "12:123",
                null,
                null,
                List.of(),
                List.of(),
                List.of(
                        new ExtractedCardVehicleUsageInterval(
                                "CVU-1",
                                OffsetDateTime.parse("2026-05-01T00:00:00Z"),
                                OffsetDateTime.parse("2026-05-01T23:59:59Z"),
                                100L,
                                200L,
                                "12:REG-1",
                                "VIN-1",
                                "a"
                        ),
                        new ExtractedCardVehicleUsageInterval(
                                "CVU-2",
                                OffsetDateTime.parse("2026-05-02T00:00:00Z"),
                                null,
                                201L,
                                null,
                                "12:REG-1",
                                "VIN-1",
                                "b"
                        )
                ),
                List.of(
                        new ExtractedCardActivityInterval(
                                "ACT-1",
                                OffsetDateTime.parse("2026-05-02T08:00:00Z"),
                                OffsetDateTime.parse("2026-05-02T09:00:00Z"),
                                "WORK",
                                "DRIVER",
                                "INSERTED",
                                "SINGLE",
                                "12:REG-1",
                                "VIN-1",
                                "c"
                        )
                ),
                List.of(),
                List.of()
        );
        TachographFileSession session = new TachographFileSession(
                UUID.randomUUID(),
                new TachographFileSessionMetadata("default", "legalrequirements-drivercard", "sample", "sample.ddd", "a", 3, "42", "b", true, null),
                Map.of(driver.driverKey(), driver),
                new ExtractionStats(1, 1, 2, 1, 1, 0),
                List.of(),
                Instant.now(),
                Instant.now().plus(4, ChronoUnit.HOURS)
        );

        ResolvedDriverTimeline timeline = builder.build(session, driver);

        assertThat(timeline.vehicleUsageIntervals()).hasSize(1);
        assertThat(timeline.vehicleUsageIntervals().get(0).sessionId()).isEqualTo(session.sessionId());
        assertThat(timeline.vehicleUsageIntervals().get(0).driverKey()).isEqualTo("12:123");
        assertThat(timeline.vehicleUsageIntervals().get(0).from()).isEqualTo(OffsetDateTime.parse("2026-05-01T00:00:00Z"));
        assertThat(timeline.vehicleUsageIntervals().get(0).to()).isNull();
        assertThat(timeline.vehicleUsageIntervals().get(0).sourceIntervalIds()).containsExactly("CVU-1", "CVU-2");
        assertThat(timeline.loadedTo()).isEqualTo(OffsetDateTime.parse("2026-05-02T09:00:00Z"));
    }

    @Test
    void buildsEsperActivityAndDrivingIntervalEventsFromResolvedTimeline() {
        DriverExtractionSession driver = new DriverExtractionSession(
                "12:123",
                null,
                null,
                List.of(),
                List.of(),
                List.of(),
                List.of(
                        new ExtractedCardActivityInterval(
                                "ACT-1",
                                OffsetDateTime.parse("2026-05-01T08:00:00Z"),
                                OffsetDateTime.parse("2026-05-01T09:00:00Z"),
                                "WORK",
                                "DRIVER",
                                "INSERTED",
                                "SINGLE",
                                "12:REG-1",
                                "VIN-1",
                                "a"
                        ),
                        new ExtractedCardActivityInterval(
                                "ACT-2",
                                OffsetDateTime.parse("2026-05-01T09:00:00Z"),
                                OffsetDateTime.parse("2026-05-01T10:30:00Z"),
                                "DRIVE",
                                "DRIVER",
                                "INSERTED",
                                "SINGLE",
                                "12:REG-1",
                                "VIN-1",
                                "b"
                        )
                ),
                List.of(),
                List.of()
        );
        TachographFileSession session = new TachographFileSession(
                UUID.randomUUID(),
                new TachographFileSessionMetadata("default", "legalrequirements-drivercard", "sample", "sample.ddd", "a", 2, "42", "b", true, null),
                Map.of(driver.driverKey(), driver),
                new ExtractionStats(1, 2, 0, 0, 0, 0),
                List.of(),
                Instant.now(),
                Instant.now().plus(4, ChronoUnit.HOURS)
        );

        List<TachographEsperActivityIntervalEvent> activityEvents =
                builder.buildEsperActivityIntervalEvents(session, driver);
        List<TachographEsperActivityIntervalEvent> drivingEvents =
                builder.buildEsperDrivingIntervalEvents(session, driver);

        assertThat(activityEvents).hasSize(2);
        assertThat(activityEvents).extracting(TachographEsperActivityIntervalEvent::activityType)
                .containsExactly("WORK", "DRIVE");
        assertThat(activityEvents).extracting(TachographEsperActivityIntervalEvent::driverKey)
                .containsOnly("12:123");

        assertThat(drivingEvents).hasSize(1);
        assertThat(drivingEvents.get(0).intervalId()).isEqualTo("ACT-2");
        assertThat(drivingEvents.get(0).activityType()).isEqualTo("DRIVE");
        assertThat(drivingEvents.get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T09:00:00Z"));
        assertThat(drivingEvents.get(0).endedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T10:30:00Z"));
    }

    @Test
    void buildsEsperVehicleUsageIntervalEventsFromResolvedTimeline() {
        DriverExtractionSession driver = new DriverExtractionSession(
                "12:123",
                null,
                null,
                List.of(),
                List.of(),
                List.of(
                        new ExtractedCardVehicleUsageInterval(
                                "CVU-1",
                                OffsetDateTime.parse("2026-05-01T08:00:00Z"),
                                OffsetDateTime.parse("2026-05-01T11:00:00Z"),
                                100L,
                                200L,
                                "12:REG-1",
                                "VIN-1",
                                "a"
                        ),
                        new ExtractedCardVehicleUsageInterval(
                                "CVU-2",
                                OffsetDateTime.parse("2026-05-01T12:00:00Z"),
                                null,
                                201L,
                                null,
                                "12:REG-1",
                                "VIN-1",
                                "b"
                        )
                ),
                List.of(),
                List.of(),
                List.of()
        );
        TachographFileSession session = new TachographFileSession(
                UUID.randomUUID(),
                new TachographFileSessionMetadata("default", "legalrequirements-drivercard", "sample", "sample.ddd", "a", 2, "42", "b", true, null),
                Map.of(driver.driverKey(), driver),
                new ExtractionStats(1, 0, 2, 0, 0, 0),
                List.of(),
                Instant.now(),
                Instant.now().plus(4, ChronoUnit.HOURS)
        );

        List<TachographEsperVehicleUsageIntervalEvent> vehicleUsageEvents =
                builder.buildEsperVehicleUsageIntervalEvents(session, driver);

        assertThat(vehicleUsageEvents).hasSize(2);
        assertThat(vehicleUsageEvents).extracting(TachographEsperVehicleUsageIntervalEvent::driverKey)
                .containsOnly("12:123");
        assertThat(vehicleUsageEvents).extracting(TachographEsperVehicleUsageIntervalEvent::sessionId)
                .containsOnly(session.sessionId());
        assertThat(vehicleUsageEvents.get(0).intervalId()).isEqualTo("CVU-1");
        assertThat(vehicleUsageEvents.get(1).intervalId()).isEqualTo("CVU-2");
        assertThat(vehicleUsageEvents.get(1).endedAt()).isNull();
    }

    @Test
    void buildsEsperVuCardAbsentIntervalEventsFromVehicleUsageGaps() {
        DriverExtractionSession driver = new DriverExtractionSession(
                "12:123",
                null,
                null,
                List.of(),
                List.of(),
                List.of(
                        new ExtractedCardVehicleUsageInterval(
                                "CVU-1",
                                OffsetDateTime.parse("2026-05-01T08:00:00Z"),
                                OffsetDateTime.parse("2026-05-01T11:00:00Z"),
                                100L,
                                200L,
                                "12:REG-1",
                                "VIN-1",
                                "a"
                        ),
                        new ExtractedCardVehicleUsageInterval(
                                "CVU-2",
                                OffsetDateTime.parse("2026-05-01T12:00:00Z"),
                                OffsetDateTime.parse("2026-05-01T13:00:00Z"),
                                201L,
                                260L,
                                "12:REG-2",
                                "VIN-2",
                                "b"
                        ),
                        new ExtractedCardVehicleUsageInterval(
                                "CVU-3",
                                OffsetDateTime.parse("2026-05-01T13:00:01Z"),
                                OffsetDateTime.parse("2026-05-01T14:00:00Z"),
                                261L,
                                320L,
                                "12:REG-2",
                                "VIN-2",
                                "c"
                        )
                ),
                List.of(),
                List.of(),
                List.of()
        );
        TachographFileSession session = new TachographFileSession(
                UUID.randomUUID(),
                new TachographFileSessionMetadata("default", "legalrequirements-drivercard", "sample", "sample.ddd", "a", 3, "42", "b", true, null),
                Map.of(driver.driverKey(), driver),
                new ExtractionStats(1, 0, 3, 0, 0, 0),
                List.of(),
                Instant.now(),
                Instant.now().plus(4, ChronoUnit.HOURS)
        );

        List<TachographEsperVuCardAbsentIntervalEvent> absentIntervals =
                builder.buildEsperVuCardAbsentIntervalEvents(session, driver);

        assertThat(absentIntervals).hasSize(1);
        assertThat(absentIntervals.get(0).sessionId()).isEqualTo(session.sessionId());
        assertThat(absentIntervals.get(0).driverKey()).isEqualTo("12:123");
        assertThat(absentIntervals.get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T11:00:01Z"));
        assertThat(absentIntervals.get(0).endedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T12:00:00Z"));
        assertThat(absentIntervals.get(0).durationSeconds()).isEqualTo(3599L);
        assertThat(absentIntervals.get(0).previousUsageIntervalId()).isEqualTo("CVU-1");
        assertThat(absentIntervals.get(0).nextUsageIntervalId()).isEqualTo("CVU-2");
        assertThat(absentIntervals.get(0).previousRegistrationKey()).isEqualTo("12:REG-1");
        assertThat(absentIntervals.get(0).nextRegistrationKey()).isEqualTo("12:REG-2");
        assertThat(absentIntervals.get(0).previousVehicleKey()).isEqualTo("VIN-1");
        assertThat(absentIntervals.get(0).nextVehicleKey()).isEqualTo("VIN-2");
    }
}
