// src/services/loadValidEvents.ts

import { stafftimeevents } from "../../../db/schema";
import {sql, and, eq, gte, lte, inArray, asc} from "drizzle-orm";
import { FastifyInstance } from "fastify";

export type TimeType = "work_start" | "work_end" | "pause_start" | "pause_end" | "sick_start" | "sick_end" | "vacation_start" | "vacation_end" | "ovetime_compensation_start" | "overtime_compensation_end";


// Die Definition des TimeEvent Typs, der zurückgegeben wird (muss mit dem tatsächlichen Typ übereinstimmen)
export type TimeEvent = {
    id: string;
    eventtype: string;
    eventtime: Date;
    actoruser_id?: string;
    related_event_id: string | null;
    payload?: Record<string, any> | null;
    created_at?: Date | null;
};

const EVENT_TYPE_ORDER: Record<string, number> = {
    auto_stop: 10,
    work_end: 10,
    pause_end: 10,
    vacation_end: 10,
    sick_end: 10,
    overtime_compensation_end: 10,
    work_start: 20,
    pause_start: 20,
    vacation_start: 20,
    sick_start: 20,
    overtime_compensation_start: 20,
    submitted: 30,
    approved: 30,
    rejected: 30,
    invalidated: 40,
};

export function compareTimeEvents(a: TimeEvent, b: TimeEvent) {
    const eventTimeDiff = a.eventtime.getTime() - b.eventtime.getTime();
    if (eventTimeDiff !== 0) return eventTimeDiff;

    const typeOrderDiff = (EVENT_TYPE_ORDER[a.eventtype] ?? 999) - (EVENT_TYPE_ORDER[b.eventtype] ?? 999);
    if (typeOrderDiff !== 0) return typeOrderDiff;

    const createdAtDiff = (a.created_at?.getTime() ?? 0) - (b.created_at?.getTime() ?? 0);
    if (createdAtDiff !== 0) return createdAtDiff;

    return a.id.localeCompare(b.id);
}

export async function loadValidEvents(
    server: FastifyInstance,
    tenantId: number,
    userId: string,
    from: Date,
    to: Date
): Promise<TimeEvent[]> {
    // Definieren Sie einen Alias für die stafftimeevents Tabelle in der äußeren Abfrage
    const baseEvents = stafftimeevents;

    // Die Subquery, um alle IDs zu finden, die ungültig gemacht wurden
    // Wir nennen die innere Tabelle 'invalidatingEvents'
    const invalidatingEvents = server.db
        .select({
            invalidatedId: baseEvents.invalidates_event_id
        })
        .from(baseEvents)
        .as('invalidating_events');

    // Die Hauptabfrage
    const result = await server.db
        .select()
        .from(baseEvents)
        .where(
            and(
                // 1. Tenant und User filtern
                eq(baseEvents.tenant_id, tenantId),
                eq(baseEvents.user_id, userId),

                // 2. Zeitbereich filtern (Typensicher)
                gte(baseEvents.eventtime, from),
                lte(baseEvents.eventtime, to),

                // 3. WICHTIG: Korrekturen ausschließen (NOT EXISTS)
                // Schließe jedes Event aus, dessen ID in der Liste der invalidates_event_id erscheint.
                sql`
                    not exists (
                        select 1
                        from ${stafftimeevents} i
                        where i.invalidates_event_id = ${baseEvents.id}
                    )
                `
            )
        )
        .orderBy(
            asc(baseEvents.eventtime),
            asc(baseEvents.created_at),
            asc(baseEvents.id)
        );

    // Mapping auf den sauberen TimeEvent Typ
    return result.map(e => ({
        id: e.id,
        eventtype: e.eventtype,
        eventtime: e.eventtime,
        actoruser_id: e.actoruser_id,
        related_event_id: e.related_event_id,
        payload: e.payload,
        created_at: e.created_at,
    })) as TimeEvent[];
}

export async function loadRelatedAdminEvents(server, eventIds) {
    if (eventIds.length === 0) return [];

    // Lädt alle administrativen Events, die sich auf die faktischen Event-IDs beziehen
    const adminEvents = await server.db
        .select()
        .from(stafftimeevents)
        .where(
            and(
                inArray(stafftimeevents.related_event_id, eventIds),
                // Wir müssen hier die Entkräftung prüfen, um z.B. einen abgelehnten submitted-Event auszuschließen
                sql`
                    not exists (
                      select 1
                      from ${stafftimeevents} i
                      where i.invalidates_event_id = ${stafftimeevents}.id
                    )
                `,
            )
        )
        // Muss nach Zeit sortiert sein, um den Status korrekt zu bestimmen!
        .orderBy(
            asc(stafftimeevents.eventtime),
            asc(stafftimeevents.created_at),
            asc(stafftimeevents.id)
        );

    return adminEvents as TimeEvent[];
}
