/*
 * Event-input adapter for tachograph-driving-derived-projection-bundle.epl.
 *
 * The old bundle consumes resolved interval input streams. This preprocessor lets the same
 * derived rules consume EventHub point events by pairing START/END activity events and
 * INSERT/WITHDRAW card-vehicle usage events inside Esper.
 *
 * Vehicle-usage intervals are additionally coalesced in EPL before they are forwarded to
 * the old interval bundle. This keeps event mode equivalent to DriverTimelineBuilder's
 * Java merge behavior for consecutive same-vehicle CardVehiclesUsed rows, including the
 * common midnight continuation 23:59:59 -> 00:00:00 on the next day.
 */

create window OpenActivityPoint#unique(driverKey, intervalId) as TachographActivityPointInputEvent;

insert into OpenActivityPoint
select *
from TachographActivityPointInputEvent(lifecycle = 'START');

@Priority(20)
on TachographActivityPointInputEvent(lifecycle = 'END') as endEvent
insert into TachographActivityIntervalInputEvent
select
    startEvent.sessionId as sessionId,
    startEvent.driverKey as driverKey,
    startEvent.intervalId as intervalId,
    startEvent.activityType as activityType,
    startEvent.cardSlot as cardSlot,
    startEvent.cardStatus as cardStatus,
    startEvent.drivingStatus as drivingStatus,
    startEvent.registrationKey as registrationKey,
    startEvent.vehicleKey as vehicleKey,
    startEvent.sourceKind as sourceKind,
    startEvent.sourceRowId as firstSourceIntervalId,
    endEvent.sourceRowId as lastSourceIntervalId,
    startEvent.occurredAt as startedAt,
    endEvent.occurredAt as endedAt,
    startEvent.occurredAtEpochSecond as startedAtEpochSecond,
    endEvent.occurredAtEpochSecond as endedAtEpochSecond,
    endEvent.occurredAtEpochSecond - startEvent.occurredAtEpochSecond as durationSeconds,
    startEvent.sourceRowIds as sourceIntervalIds,
    startEvent.synthetic as synthetic,
    startEvent.clippedToRequestedPeriod as clippedToRequestedPeriod,
    startEvent.level as level
from OpenActivityPoint as startEvent
where startEvent.driverKey = endEvent.driverKey
  and startEvent.intervalId = endEvent.intervalId
  and endEvent.occurredAtEpochSecond > startEvent.occurredAtEpochSecond;

@Priority(10)
on TachographActivityPointInputEvent(lifecycle = 'END') as endEvent
delete from OpenActivityPoint as openEvent
where openEvent.driverKey = endEvent.driverKey
  and openEvent.intervalId = endEvent.intervalId;

create schema RawVehicleUsageInterval(
    sessionId java.util.UUID,
    driverKey string,
    intervalId string,
    firstSourceIntervalId string,
    lastSourceIntervalId string,
    startedAt java.time.OffsetDateTime,
    endedAt java.time.OffsetDateTime,
    startedAtEpochSecond long,
    endedAtEpochSecond java.lang.Long,
    durationSeconds long,
    odometerBeginKm java.lang.Long,
    odometerEndKm java.lang.Long,
    registrationKey string,
    vehicleKey string,
    sourceKind string,
    sourceIntervalIds java.util.List
);

create window OpenVehicleUsagePoint#unique(driverKey, intervalId) as TachographVehicleUsagePointInputEvent;

insert into OpenVehicleUsagePoint
select *
from TachographVehicleUsagePointInputEvent(lifecycle = 'INSERT');

@Priority(20)
on TachographVehicleUsagePointInputEvent(lifecycle = 'WITHDRAW') as withdrawEvent
insert into RawVehicleUsageInterval
select
    insertEvent.sessionId as sessionId,
    insertEvent.driverKey as driverKey,
    insertEvent.intervalId as intervalId,
    insertEvent.sourceRowId as firstSourceIntervalId,
    withdrawEvent.sourceRowId as lastSourceIntervalId,
    insertEvent.occurredAt as startedAt,
    withdrawEvent.occurredAt as endedAt,
    insertEvent.occurredAtEpochSecond as startedAtEpochSecond,
    withdrawEvent.occurredAtEpochSecond as endedAtEpochSecond,
    withdrawEvent.occurredAtEpochSecond - insertEvent.occurredAtEpochSecond as durationSeconds,
    insertEvent.odometerKm as odometerBeginKm,
    withdrawEvent.odometerKm as odometerEndKm,
    insertEvent.registrationKey as registrationKey,
    insertEvent.vehicleKey as vehicleKey,
    insertEvent.sourceKind as sourceKind,
    insertEvent.sourceRowIds as sourceIntervalIds
from OpenVehicleUsagePoint as insertEvent
where insertEvent.driverKey = withdrawEvent.driverKey
  and insertEvent.intervalId = withdrawEvent.intervalId
  and withdrawEvent.occurredAtEpochSecond > insertEvent.occurredAtEpochSecond;

@Priority(10)
on TachographVehicleUsagePointInputEvent(lifecycle = 'WITHDRAW') as withdrawEvent
delete from OpenVehicleUsagePoint as openEvent
where openEvent.driverKey = withdrawEvent.driverKey
  and openEvent.intervalId = withdrawEvent.intervalId;

create window MergedVehicleUsageAccumulator#unique(driverKey) as RawVehicleUsageInterval;
create window MergedVehicleUsageNext#unique(driverKey) as RawVehicleUsageInterval;

/*
 * Case 1: first vehicle-usage interval for this driver. Start a new accumulator.
 */
@Priority(70)
on RawVehicleUsageInterval as next
insert into MergedVehicleUsageNext
select
    next.sessionId as sessionId,
    next.driverKey as driverKey,
    next.intervalId as intervalId,
    next.firstSourceIntervalId as firstSourceIntervalId,
    next.lastSourceIntervalId as lastSourceIntervalId,
    next.startedAt as startedAt,
    next.endedAt as endedAt,
    next.startedAtEpochSecond as startedAtEpochSecond,
    next.endedAtEpochSecond as endedAtEpochSecond,
    next.durationSeconds as durationSeconds,
    next.odometerBeginKm as odometerBeginKm,
    next.odometerEndKm as odometerEndKm,
    next.registrationKey as registrationKey,
    next.vehicleKey as vehicleKey,
    next.sourceKind as sourceKind,
    next.sourceIntervalIds as sourceIntervalIds
where not exists (
    select * from MergedVehicleUsageAccumulator as current
    where current.driverKey = next.driverKey
);

/*
 * Case 2: same vehicle and registration, and the next interval starts at or before
 * current.to + 1 second. This includes 23:59:59 -> 00:00:00. Keep one accumulated
 * interval and extend its end/last-source/ending odometer.
 */
@Priority(70)
on RawVehicleUsageInterval as next
insert into MergedVehicleUsageNext
select
    current.sessionId as sessionId,
    current.driverKey as driverKey,
    current.intervalId as intervalId,
    current.firstSourceIntervalId as firstSourceIntervalId,
    next.lastSourceIntervalId as lastSourceIntervalId,
    current.startedAt as startedAt,
    next.endedAt as endedAt,
    current.startedAtEpochSecond as startedAtEpochSecond,
    next.endedAtEpochSecond as endedAtEpochSecond,
    next.endedAtEpochSecond - current.startedAtEpochSecond as durationSeconds,
    current.odometerBeginKm as odometerBeginKm,
    next.odometerEndKm as odometerEndKm,
    current.registrationKey as registrationKey,
    current.vehicleKey as vehicleKey,
    current.sourceKind as sourceKind,
    current.sourceIntervalIds as sourceIntervalIds
from MergedVehicleUsageAccumulator as current
where current.driverKey = next.driverKey
  and current.endedAtEpochSecond is not null
  and next.startedAtEpochSecond <= current.endedAtEpochSecond + 1L
  and (
        (current.registrationKey is null and next.registrationKey is null)
        or (
            current.registrationKey is not null
        and next.registrationKey is not null
        and current.registrationKey = next.registrationKey
        )
  )
  and (
        (current.vehicleKey is null and next.vehicleKey is null)
        or (
            current.vehicleKey is not null
        and next.vehicleKey is not null
        and current.vehicleKey = next.vehicleKey
        )
  );

/*
 * Case 3a: next interval is not mergeable. Emit the accumulated interval to the old
 * interval-based bundle.
 */
@Priority(70)
on RawVehicleUsageInterval as next
insert into TachographVehicleUsageIntervalInputEvent
select
    current.sessionId as sessionId,
    current.driverKey as driverKey,
    current.intervalId as intervalId,
    current.firstSourceIntervalId as firstSourceIntervalId,
    current.lastSourceIntervalId as lastSourceIntervalId,
    current.startedAt as startedAt,
    current.endedAt as endedAt,
    current.startedAtEpochSecond as startedAtEpochSecond,
    current.endedAtEpochSecond as endedAtEpochSecond,
    current.durationSeconds as durationSeconds,
    current.odometerBeginKm as odometerBeginKm,
    current.odometerEndKm as odometerEndKm,
    current.registrationKey as registrationKey,
    current.vehicleKey as vehicleKey,
    current.sourceKind as sourceKind,
    current.sourceIntervalIds as sourceIntervalIds
from MergedVehicleUsageAccumulator as current
where current.driverKey = next.driverKey
  and not (
        current.endedAtEpochSecond is not null
    and next.startedAtEpochSecond <= current.endedAtEpochSecond + 1L
    and (
            (current.registrationKey is null and next.registrationKey is null)
            or (
                current.registrationKey is not null
            and next.registrationKey is not null
            and current.registrationKey = next.registrationKey
            )
    )
    and (
            (current.vehicleKey is null and next.vehicleKey is null)
            or (
                current.vehicleKey is not null
            and next.vehicleKey is not null
            and current.vehicleKey = next.vehicleKey
            )
    )
  );

/*
 * Case 3b: next interval is not mergeable. Start a new accumulator with it.
 */
@Priority(70)
on RawVehicleUsageInterval as next
insert into MergedVehicleUsageNext
select
    next.sessionId as sessionId,
    next.driverKey as driverKey,
    next.intervalId as intervalId,
    next.firstSourceIntervalId as firstSourceIntervalId,
    next.lastSourceIntervalId as lastSourceIntervalId,
    next.startedAt as startedAt,
    next.endedAt as endedAt,
    next.startedAtEpochSecond as startedAtEpochSecond,
    next.endedAtEpochSecond as endedAtEpochSecond,
    next.durationSeconds as durationSeconds,
    next.odometerBeginKm as odometerBeginKm,
    next.odometerEndKm as odometerEndKm,
    next.registrationKey as registrationKey,
    next.vehicleKey as vehicleKey,
    next.sourceKind as sourceKind,
    next.sourceIntervalIds as sourceIntervalIds
where exists (
    select * from MergedVehicleUsageAccumulator as current
    where current.driverKey = next.driverKey
      and not (
            current.endedAtEpochSecond is not null
        and next.startedAtEpochSecond <= current.endedAtEpochSecond + 1L
        and (
                (current.registrationKey is null and next.registrationKey is null)
                or (
                    current.registrationKey is not null
                and next.registrationKey is not null
                and current.registrationKey = next.registrationKey
                )
        )
        and (
                (current.vehicleKey is null and next.vehicleKey is null)
                or (
                    current.vehicleKey is not null
                and next.vehicleKey is not null
                and current.vehicleKey = next.vehicleKey
                )
        )
      )
);

@Priority(60)
on RawVehicleUsageInterval as next
delete from MergedVehicleUsageAccumulator as current
where current.driverKey = next.driverKey;

@Priority(50)
on RawVehicleUsageInterval as next
insert into MergedVehicleUsageAccumulator
select
    candidate.sessionId as sessionId,
    candidate.driverKey as driverKey,
    candidate.intervalId as intervalId,
    candidate.firstSourceIntervalId as firstSourceIntervalId,
    candidate.lastSourceIntervalId as lastSourceIntervalId,
    candidate.startedAt as startedAt,
    candidate.endedAt as endedAt,
    candidate.startedAtEpochSecond as startedAtEpochSecond,
    candidate.endedAtEpochSecond as endedAtEpochSecond,
    candidate.durationSeconds as durationSeconds,
    candidate.odometerBeginKm as odometerBeginKm,
    candidate.odometerEndKm as odometerEndKm,
    candidate.registrationKey as registrationKey,
    candidate.vehicleKey as vehicleKey,
    candidate.sourceKind as sourceKind,
    candidate.sourceIntervalIds as sourceIntervalIds
from MergedVehicleUsageNext as candidate
where candidate.driverKey = next.driverKey;

@Priority(40)
on RawVehicleUsageInterval as next
delete from MergedVehicleUsageNext as candidate
where candidate.driverKey = next.driverKey;

/*
 * The last accumulated interval cannot be emitted by looking at the next interval, so Java
 * sends one TachographProjectionFinalizeEvent per driver after all vehicle-usage point
 * events and before activity point events.
 */
@Priority(20)
on TachographProjectionFinalizeEvent as finalizeEvent
insert into TachographVehicleUsageIntervalInputEvent
select
    current.sessionId as sessionId,
    current.driverKey as driverKey,
    current.intervalId as intervalId,
    current.firstSourceIntervalId as firstSourceIntervalId,
    current.lastSourceIntervalId as lastSourceIntervalId,
    current.startedAt as startedAt,
    current.endedAt as endedAt,
    current.startedAtEpochSecond as startedAtEpochSecond,
    current.endedAtEpochSecond as endedAtEpochSecond,
    current.durationSeconds as durationSeconds,
    current.odometerBeginKm as odometerBeginKm,
    current.odometerEndKm as odometerEndKm,
    current.registrationKey as registrationKey,
    current.vehicleKey as vehicleKey,
    current.sourceKind as sourceKind,
    current.sourceIntervalIds as sourceIntervalIds
from MergedVehicleUsageAccumulator as current
where current.driverKey = finalizeEvent.driverKey;

@Priority(10)
on TachographProjectionFinalizeEvent as finalizeEvent
delete from MergedVehicleUsageAccumulator as current
where current.driverKey = finalizeEvent.driverKey;
