package at.procon.eventhub.tachographfilesession.service;

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

import at.procon.eventhub.dto.EventDomain;
import at.procon.eventhub.dto.EventLifecycle;
import at.procon.eventhub.dto.EventType;
import at.procon.eventhub.service.EventDetailsFactory;
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.ExtractedDriver;
import at.procon.eventhub.tachographfilesession.model.ExtractedDriverCard;
import at.procon.eventhub.tachographfilesession.model.ExtractedSupportEvent;
import at.procon.eventhub.tachographfilesession.model.ExtractedVehicle;
import at.procon.eventhub.tachographfilesession.model.ExtractedVehicleRegistration;
import at.procon.eventhub.tachographfilesession.model.ExtractionStats;
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
import at.procon.eventhub.tachographfilesession.model.TachographFileSessionMetadata;
import at.procon.eventhub.tachographfilesession.model.TachographTimelineEventBundle;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.math.BigDecimal;
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 IntervalBackedDriverTimelineEventBuilderTest {

    private final IntervalBackedDriverTimelineEventBuilder builder = new IntervalBackedDriverTimelineEventBuilder(
            new DriverTimelineBuilder(),
            new DriverKeyFactory(),
            new VehicleKeyFactory(),
            new EventDetailsFactory(new ObjectMapper())
    );

    @Test
    void buildsLifecycleActivityEventsFromIntervals() {
        DriverExtractionSession driver = new DriverExtractionSession(
                "12:123",
                new ExtractedDriver("12:123", "DRV:12:123", "Doe", "Jane", null, null, null, null, null),
                new ExtractedDriverCard("CARD:12:123", "12", "123", null, null, null, null),
                List.of(new ExtractedVehicleRegistration("12:REG-1", "VR:12:REG-1", "12", "REG-1")),
                List.of(new ExtractedVehicle("VIN-1", "VIN:VIN-1", "VIN-1")),
                List.of(new ExtractedCardVehicleUsageInterval(
                        "CVU-1",
                        OffsetDateTime.parse("2026-05-01T08:00:00Z"),
                        OffsetDateTime.parse("2026-05-01T10:00:00Z"),
                        100L,
                        200L,
                        "12:REG-1",
                        "VIN-1",
                        "vu-1"
                )),
                List.of(new ExtractedCardActivityInterval(
                        "ACT-1",
                        OffsetDateTime.parse("2026-05-01T08:30:00Z"),
                        OffsetDateTime.parse("2026-05-01T09:00:00Z"),
                        "DRIVE",
                        "DRIVER",
                        "INSERTED",
                        "SINGLE",
                        "12:REG-1",
                        "VIN-1",
                        "a"
                )),
                List.of(),
                List.of()
        );
        TachographFileSession session = session(driver, true);

        TachographTimelineEventBundle bundle = builder.buildEventBundle(session, driver);

        assertThat(bundle.activityEvents()).hasSize(2);
        assertThat(bundle.activityEvents().get(0).eventDomain()).isEqualTo(EventDomain.DRIVER_ACTIVITY);
        assertThat(bundle.activityEvents().get(0).eventType()).isEqualTo(EventType.DRIVE);
        assertThat(bundle.activityEvents().get(0).lifecycle()).isEqualTo(EventLifecycle.START);
        assertThat(bundle.activityEvents().get(0).occurredAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T08:30:00Z"));
        assertThat(bundle.activityEvents().get(0).driverRef().sourceEntityId()).isEqualTo("DRV:12:123");
        assertThat(bundle.activityEvents().get(0).vehicleRef().vin()).isEqualTo("VIN-1");
        assertThat(bundle.activityEvents().get(0).eventDetails().type()).isEqualTo("DRIVER_ACTIVITY");
        assertThat(bundle.activityEvents().get(0).payload().get("raw").get("intervalId").asText()).isEqualTo("ACT-1");
        assertThat(bundle.activityEvents().get(1).lifecycle()).isEqualTo(EventLifecycle.END);
        assertThat(bundle.activityEvents().get(1).occurredAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T09:00:00Z"));
    }

    @Test
    void buildsVehicleUsageAndSupportEvents() {
        DriverExtractionSession driver = new DriverExtractionSession(
                "12:123",
                new ExtractedDriver("12:123", "DRV:12:123", "Doe", "Jane", null, null, null, null, null),
                new ExtractedDriverCard("CARD:12:123", "12", "123", null, null, null, null),
                List.of(new ExtractedVehicleRegistration("12:REG-1", "VR:12:REG-1", "12", "REG-1")),
                List.of(new ExtractedVehicle("VIN-1", "VIN:VIN-1", "VIN-1")),
                List.of(new ExtractedCardVehicleUsageInterval(
                        "CVU-1",
                        OffsetDateTime.parse("2026-05-01T08:00:00Z"),
                        OffsetDateTime.parse("2026-05-01T10:00:00Z"),
                        100L,
                        200L,
                        "12:REG-1",
                        "VIN-1",
                        "vu-1"
                )),
                List.of(),
                List.of(new ExtractedSupportEvent(
                        "VUGNSS-1",
                        OffsetDateTime.parse("2026-05-01T08:45:00Z"),
                        "POSITION",
                        "POSITION_RECORDED",
                        "SNAPSHOT",
                        "DRIVER",
                        "12:REG-1",
                        "VIN-1",
                        null,
                        null,
                        null,
                        null,
                        null,
                        BigDecimal.valueOf(48.2082),
                        BigDecimal.valueOf(16.3738),
                        "AUTHENTIC",
                        150L,
                        null,
                        null,
                        null,
                        "raw-path"
                )),
                List.of()
        );
        TachographFileSession session = session(driver, false);

        TachographTimelineEventBundle bundle = builder.buildEventBundle(session, driver);

        assertThat(bundle.vehicleUsageEvents()).hasSize(2);
        assertThat(bundle.vehicleUsageEvents().get(0).eventDomain()).isEqualTo(EventDomain.DRIVER_CARD);
        assertThat(bundle.vehicleUsageEvents().get(0).eventType()).isEqualTo(EventType.CARD_INSERTED);
        assertThat(bundle.vehicleUsageEvents().get(0).lifecycle()).isEqualTo(EventLifecycle.INSERT);
        assertThat(bundle.vehicleUsageEvents().get(0).odometerM()).isEqualTo(100_000L);
        assertThat(bundle.vehicleUsageEvents().get(1).eventType()).isEqualTo(EventType.CARD_WITHDRAWN);
        assertThat(bundle.vehicleUsageEvents().get(1).lifecycle()).isEqualTo(EventLifecycle.WITHDRAW);
        assertThat(bundle.vehicleUsageEvents().get(1).odometerM()).isEqualTo(200_000L);

        assertThat(bundle.supportEvents()).hasSize(1);
        assertThat(bundle.supportEvents().get(0).eventDomain()).isEqualTo(EventDomain.POSITION);
        assertThat(bundle.supportEvents().get(0).eventType()).isEqualTo(EventType.POSITION_RECORDED);
        assertThat(bundle.supportEvents().get(0).position().latitude()).isEqualTo(BigDecimal.valueOf(48.2082));
        assertThat(bundle.supportEvents().get(0).eventDetails().type()).isEqualTo("POSITION");

        assertThat(bundle.allEvents()).extracting(event -> event.occurredAt()).isSorted();
    }

    private TachographFileSession session(DriverExtractionSession driver, boolean driverCardFile) {
        return new TachographFileSession(
                UUID.randomUUID(),
                new TachographFileSessionMetadata(
                        "default",
                        "legalrequirements-drivercard",
                        "sample",
                        "sample.ddd",
                        "a",
                        2,
                        "42",
                        "b",
                        driverCardFile,
                        null
                ),
                Map.of(driver.driverKey(), driver),
                new ExtractionStats(
                        1,
                        driver.cardActivityIntervals().size(),
                        driver.cardVehicleUsageIntervals().size(),
                        driver.vehicleRegistrations().size(),
                        driver.vehicles().size(),
                        0
                ),
                List.of(),
                Instant.now(),
                Instant.now().plus(4, ChronoUnit.HOURS)
        );
    }
}
