import { disconnectClient, getClientConfigVersion } from "#dynamic/routers/ws";
import { db } from "@server/db";
import { clients } from "@server/db";
import { eq, lt, isNull, and, or } from "drizzle-orm";
import logger from "@server/logger";
import { sendTerminateClient } from "../client/terminate";
import { OlmErrorCodes } from "./error";

// Track if the offline checker interval is running
let offlineCheckerInterval: NodeJS.Timeout | null = null;
const OFFLINE_CHECK_INTERVAL = 30 * 1000; // Check every 30 seconds
const OFFLINE_THRESHOLD_MS = 2 * 60 * 1000; // 2 minutes

/**
 * Starts the background interval that checks for clients that haven't pinged recently
 * and marks them as offline
 */
export const startOlmOfflineChecker = (): void => {
    if (offlineCheckerInterval) {
        return; // Already running
    }

    offlineCheckerInterval = setInterval(async () => {
        try {
            const twoMinutesAgo = Math.floor(
                (Date.now() - OFFLINE_THRESHOLD_MS) / 1000
            );

            // TODO: WE NEED TO MAKE SURE THIS WORKS WITH DISTRIBUTED NODES ALL DOING THE SAME THING

            // Find clients that haven't pinged in the last 2 minutes and mark them as offline
            const offlineClients = await db
                .update(clients)
                .set({ online: false })
                .where(
                    and(
                        eq(clients.online, true),
                        or(
                            lt(clients.lastPing, twoMinutesAgo),
                            isNull(clients.lastPing)
                        )
                    )
                )
                .returning();

            for (const offlineClient of offlineClients) {
                logger.info(
                    `Kicking offline olm client ${offlineClient.clientId} due to inactivity`
                );

                if (!offlineClient.olmId) {
                    logger.warn(
                        `Offline client ${offlineClient.clientId} has no olmId, cannot disconnect`
                    );
                    continue;
                }

                // Send a disconnect message to the client if connected
                try {
                    await sendTerminateClient(
                        offlineClient.clientId,
                        OlmErrorCodes.TERMINATED_INACTIVITY,
                        offlineClient.olmId
                    ); // terminate first
                    // wait a moment to ensure the message is sent
                    await new Promise((resolve) => setTimeout(resolve, 1000));
                    await disconnectClient(offlineClient.olmId);
                } catch (error) {
                    logger.error(
                        `Error sending disconnect to offline olm ${offlineClient.clientId}`,
                        { error }
                    );
                }
            }
        } catch (error) {
            logger.error("Error in offline checker interval", { error });
        }
    }, OFFLINE_CHECK_INTERVAL);

    logger.debug("Started offline checker interval");
};

/**
 * Stops the background interval that checks for offline clients
 */
export const stopOlmOfflineChecker = (): void => {
    if (offlineCheckerInterval) {
        clearInterval(offlineCheckerInterval);
        offlineCheckerInterval = null;
        logger.info("Stopped offline checker interval");
    }
};
