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.TachographFileSession;
import at.procon.eventhub.tachographfilesession.model.TachographFileSessionMetadata;
import java.io.StringReader;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import javax.xml.parsers.DocumentBuilderFactory;
import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;

class VehicleUnitXmlExtractionServiceTest {

    private final VehicleUnitXmlExtractionService service = new VehicleUnitXmlExtractionService(
            new DriverKeyFactory(),
            new VehicleKeyFactory()
    );

    @Test
    void extractsVehicleUnitDriverSessionsFromCardInsertionWithdrawalData() throws Exception {
        TachographFileSession session = service.extract(
                new TachographXmlParser.ParsedTachographXml(document(VehicleUnitXmlSamples.vehicleUnitXml()), "VehicleUnit"),
                new TachographFileSessionMetadata(
                        "default",
                        "legalrequirements-vehicleunit",
                        "sample-vu",
                        "sample-vu.ddd",
                        "abc",
                        10,
                        "42",
                        "def",
                        false,
                        null
                ),
                Instant.now(),
                Instant.now().plus(4, ChronoUnit.HOURS)
        );

        assertThat(session.driversByKey()).hasSize(2);
        DriverExtractionSession firstDriver = session.driversByKey().get("12:12345678901200");
        DriverExtractionSession secondDriver = session.driversByKey().get("12:99999999999911");
        assertThat(firstDriver).isNotNull();
        assertThat(secondDriver).isNotNull();

        assertThat(firstDriver.driver().surname()).isEqualTo("Muster");
        assertThat(firstDriver.vehicleRegistrations()).extracting("registrationKey").containsExactly("12:W-1000V");
        assertThat(firstDriver.vehicles()).extracting("vehicleKey").containsExactly("VINVU123456789012");
        assertThat(firstDriver.cardVehicleUsageIntervals()).hasSize(1);
        assertThat(firstDriver.cardVehicleUsageIntervals().get(0).from().toString()).isEqualTo("2026-04-01T08:00Z");
        assertThat(firstDriver.cardVehicleUsageIntervals().get(0).to().toString()).isEqualTo("2026-04-01T11:00Z");
        assertThat(firstDriver.cardActivityIntervals()).hasSize(2);
        assertThat(firstDriver.cardActivityIntervals().get(0).activityType()).isEqualTo("WORK");
        assertThat(firstDriver.cardActivityIntervals().get(1).activityType()).isEqualTo("DRIVE");
        assertThat(firstDriver.supportEvents()).hasSize(1);
        assertThat(firstDriver.supportEvents().get(0).eventDomain()).isEqualTo("PLACE");
        assertThat(firstDriver.supportEvents().get(0).eventType()).isEqualTo("BEGIN_DAILY_WORK_PERIOD");
        assertThat(firstDriver.supportEvents().get(0).country()).isEqualTo("12");
        assertThat(firstDriver.supportEvents().get(0).latitude()).isNotNull();

        assertThat(secondDriver.cardVehicleUsageIntervals()).hasSize(1);
        assertThat(secondDriver.cardVehicleUsageIntervals().get(0).to()).isNull();
        assertThat(secondDriver.cardActivityIntervals()).hasSize(1);
        assertThat(secondDriver.cardActivityIntervals().get(0).from().toString()).isEqualTo("2026-04-02T07:30Z");
        assertThat(secondDriver.supportEvents()).hasSize(6);
        assertThat(secondDriver.supportEvents()).extracting("eventDomain")
                .containsExactly("POSITION", "SPECIFIC_CONDITION", "BORDER_CROSSING", "LOAD_UNLOAD", "SPEEDING", "SPEEDING");
        assertThat(secondDriver.supportEvents().get(0).latitude()).isNotNull();
        assertThat(secondDriver.supportEvents().get(1).eventType()).isEqualTo("OUT");
        assertThat(secondDriver.supportEvents().get(1).eventLifecycle()).isEqualTo("BEGIN");
        assertThat(secondDriver.supportEvents().get(2).countryFrom()).isEqualTo("12");
        assertThat(secondDriver.supportEvents().get(2).countryTo()).isEqualTo("56");
        assertThat(secondDriver.supportEvents().get(3).operation()).isEqualTo("LOAD");
        assertThat(secondDriver.supportEvents().get(4).eventLifecycle()).isEqualTo("BEGIN");
        assertThat(secondDriver.supportEvents().get(5).eventLifecycle()).isEqualTo("END");

        assertThat(session.warnings()).isEmpty();
    }

    @Test
    void extractsActivitiesWhenVuTimeOfChangeHasNoOffset() throws Exception {
        TachographFileSession session = service.extract(
                new TachographXmlParser.ParsedTachographXml(document(VehicleUnitXmlSamples.vehicleUnitXmlWithLocalActivityTimes()), "VehicleUnit"),
                new TachographFileSessionMetadata(
                        "default",
                        "legalrequirements-vehicleunit",
                        "sample-vu",
                        "sample-vu.ddd",
                        "abc",
                        10,
                        "42",
                        "def",
                        false,
                        null
                ),
                Instant.now(),
                Instant.now().plus(4, ChronoUnit.HOURS)
        );

        DriverExtractionSession firstDriver = session.driversByKey().get("12:12345678901200");
        DriverExtractionSession secondDriver = session.driversByKey().get("12:99999999999911");
        assertThat(firstDriver.cardActivityIntervals().get(0).from().toString()).isEqualTo("2026-04-01T08:00Z");
        assertThat(secondDriver.cardActivityIntervals().get(0).from().toString()).isEqualTo("2026-04-02T07:30Z");
    }

    @Test
    void keepsOpenVuCardUsageRecordWithoutClosingItAtDownloadPeriodEnd() throws Exception {
        TachographFileSession session = service.extract(
                new TachographXmlParser.ParsedTachographXml(document(VehicleUnitXmlSamples.vehicleUnitXml()), "VehicleUnit"),
                new TachographFileSessionMetadata(
                        "default",
                        "legalrequirements-vehicleunit",
                        "sample-vu",
                        "sample-vu.ddd",
                        "abc",
                        10,
                        "42",
                        "def",
                        false,
                        null
                ),
                Instant.now(),
                Instant.now().plus(4, ChronoUnit.HOURS)
        );

        DriverExtractionSession secondDriver = session.driversByKey().get("12:99999999999911");
        assertThat(secondDriver.cardVehicleUsageIntervals()).hasSize(1);
        assertThat(secondDriver.cardVehicleUsageIntervals().get(0).from().toString()).isEqualTo("2026-04-02T07:30Z");
        assertThat(secondDriver.cardVehicleUsageIntervals().get(0).to()).isNull();
    }

    @Test
    void ignoresSupportEventsWhenVehicleRegistrationIsUnknown() throws Exception {
        String xml = VehicleUnitXmlSamples.vehicleUnitXml()
                .replace("<vehicleRegNumber>W-1000V</vehicleRegNumber>", "<vehicleRegNumber></vehicleRegNumber>");

        TachographFileSession session = service.extract(
                new TachographXmlParser.ParsedTachographXml(document(xml), "VehicleUnit"),
                new TachographFileSessionMetadata(
                        "default",
                        "legalrequirements-vehicleunit",
                        "sample-vu",
                        "sample-vu.ddd",
                        "abc",
                        10,
                        "42",
                        "def",
                        false,
                        null
                ),
                Instant.now(),
                Instant.now().plus(4, ChronoUnit.HOURS)
        );

        assertThat(session.driversByKey()).hasSize(2);
        assertThat(session.driversByKey().values())
                .allSatisfy(driver -> assertThat(driver.supportEvents()).isEmpty());
    }

    @Test
    void usesCanonicalCardNumberForVehicleUnitDriverIdentityAndKeepsFullCardNumberAsEvidence() throws Exception {
        String xml = VehicleUnitXmlSamples.vehicleUnitXml()
                .replaceFirst("<driverIdentification>123456789012</driverIdentification>", "<driverIdentification>12345678901234</driverIdentification>")
                .replaceFirst("<cardReplacementIndex>0</cardReplacementIndex>", "<cardReplacementIndex>5</cardReplacementIndex>")
                .replaceFirst("<cardRenewalIndex>0</cardRenewalIndex>", "<cardRenewalIndex>7</cardRenewalIndex>");

        TachographFileSession session = service.extract(
                new TachographXmlParser.ParsedTachographXml(document(xml), "VehicleUnit"),
                new TachographFileSessionMetadata(
                        "default",
                        "legalrequirements-vehicleunit",
                        "sample-vu",
                        "sample-vu.ddd",
                        "abc",
                        10,
                        "42",
                        "def",
                        false,
                        null
                ),
                Instant.now(),
                Instant.now().plus(4, ChronoUnit.HOURS)
        );

        DriverExtractionSession firstDriver = session.driversByKey().get("12:12345678901234");
        assertThat(firstDriver).isNotNull();
        assertThat(firstDriver.driverCard().cardNumber()).isEqualTo("12345678901234");
        assertThat(firstDriver.driverCard().fullCardNumber()).isEqualTo("1234567890123457");
        assertThat(firstDriver.driverCard().sourceDriverCardId()).isEqualTo("CARD:12:12345678901234");
    }

    private Document document(String xml) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(false);
        return factory.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
    }
}
