import { $replica } from "~/db.server";
import { env } from "~/env.server";
import { devPresence } from "~/presenters/v3/DevPresence.server";
import { logger } from "~/services/logger.server";
import { requireUserId } from "~/services/session.server";
import { EnvironmentParamSchema } from "~/utils/pathBuilder";
import { createSSELoader, type SendFunction } from "~/utils/sse";

export const loader = createSSELoader({
  timeout: env.DEV_PRESENCE_SSE_TIMEOUT,
  interval: env.DEV_PRESENCE_POLL_MS,
  debug: false,
  handler: async ({ id, controller, debug, request, params }) => {
    const userId = await requireUserId(request);
    const { organizationSlug, projectParam, envParam } = EnvironmentParamSchema.parse(params);

    const environment = await $replica.runtimeEnvironment.findFirst({
      where: {
        type: "DEVELOPMENT",
        slug: envParam,
        orgMember: {
          userId,
        },
        project: {
          slug: projectParam,
          organization: { slug: organizationSlug },
        },
      },
    });

    if (!environment) {
      throw new Response("Not Found", { status: 404 });
    }

    const checkAndSendPresence = async (send: SendFunction) => {
      try {
        // Use the command client for the GET operation
        const isConnected = await devPresence.isConnected(environment.id);

        send({
          event: "presence",
          data: JSON.stringify({
            isConnected,
            environmentId: environment.id,
            timestamp: new Date().toISOString(),
          }),
        });

        return isConnected;
      } catch (error) {
        // Handle the case where the controller is closed
        logger.debug("Failed to send presence data, stream might be closed", { error });
        return false;
      }
    };

    return {
      beforeStream: async () => {
        logger.debug("Start dev presence listening SSE session", {
          environmentId: environment.id,
        });
      },
      initStream: async ({ send }) => {
        await checkAndSendPresence(send);

        send({ event: "time", data: new Date().toISOString() });
      },
      iterator: async ({ send, date }) => {
        await checkAndSendPresence(send);
      },
      cleanup: async ({ send }) => {
        await checkAndSendPresence(send);
      },
    };
  },
});
