import { Worker as RedisWorker } from "@trigger.dev/redis-worker";
import { Logger } from "@trigger.dev/core/logger";
import { z } from "zod";
import { env } from "~/env.server";
import { logger } from "~/services/logger.server";
import { singleton } from "~/utils/singleton";
import { TaskRunHeartbeatFailedService } from "./taskRunHeartbeatFailed.server";
import { completeBatchTaskRunItemV3, tryCompleteBatchV3 } from "./services/batchTriggerV3.server";
import { prisma } from "~/db.server";
import { marqs } from "./marqs/index.server";

function initializeWorker() {
  const redisOptions = {
    keyPrefix: "legacy-run-engine:worker:",
    host: env.LEGACY_RUN_ENGINE_WORKER_REDIS_HOST,
    port: env.LEGACY_RUN_ENGINE_WORKER_REDIS_PORT,
    username: env.LEGACY_RUN_ENGINE_WORKER_REDIS_USERNAME,
    password: env.LEGACY_RUN_ENGINE_WORKER_REDIS_PASSWORD,
    enableAutoPipelining: true,
    ...(env.LEGACY_RUN_ENGINE_WORKER_REDIS_TLS_DISABLED === "true" ? {} : { tls: {} }),
  };

  logger.debug(
    `👨‍🏭 Initializing legacy run engine worker at host ${env.LEGACY_RUN_ENGINE_WORKER_REDIS_HOST}`
  );

  const worker = new RedisWorker({
    name: "legacy-run-engine-worker",
    redisOptions,
    catalog: {
      runHeartbeat: {
        schema: z.object({
          runId: z.string(),
        }),
        visibilityTimeoutMs: 60_000,
        retry: {
          maxAttempts: 3,
        },
      },
      completeBatchTaskRunItem: {
        schema: z.object({
          itemId: z.string(),
          batchTaskRunId: z.string(),
          scheduleResumeOnComplete: z.boolean(),
          taskRunAttemptId: z.string().optional(),
          attempt: z.number().optional(),
        }),
        visibilityTimeoutMs: 60_000,
        retry: {
          maxAttempts: 10,
        },
      },
      tryCompleteBatchV3: {
        schema: z.object({
          batchId: z.string(),
          scheduleResumeOnComplete: z.boolean(),
        }),
        visibilityTimeoutMs: 30_000,
        retry: {
          maxAttempts: 5,
        },
      },
      scheduleRequeueMessage: {
        schema: z.object({
          messageId: z.string(),
        }),
        visibilityTimeoutMs: 60_000,
        retry: {
          maxAttempts: 5,
        },
      },
    },
    concurrency: {
      workers: env.LEGACY_RUN_ENGINE_WORKER_CONCURRENCY_WORKERS,
      tasksPerWorker: env.LEGACY_RUN_ENGINE_WORKER_CONCURRENCY_TASKS_PER_WORKER,
      limit: env.LEGACY_RUN_ENGINE_WORKER_CONCURRENCY_LIMIT,
    },
    pollIntervalMs: env.LEGACY_RUN_ENGINE_WORKER_POLL_INTERVAL,
    immediatePollIntervalMs: env.LEGACY_RUN_ENGINE_WORKER_IMMEDIATE_POLL_INTERVAL,
    shutdownTimeoutMs: env.LEGACY_RUN_ENGINE_WORKER_SHUTDOWN_TIMEOUT_MS,
    logger: new Logger("LegacyRunEngineWorker", env.LEGACY_RUN_ENGINE_WORKER_LOG_LEVEL),
    jobs: {
      runHeartbeat: async ({ payload }) => {
        const service = new TaskRunHeartbeatFailedService();

        await service.call(payload.runId);
      },
      completeBatchTaskRunItem: async ({ payload, attempt }) => {
        await completeBatchTaskRunItemV3(
          payload.itemId,
          payload.batchTaskRunId,
          prisma,
          payload.scheduleResumeOnComplete,
          payload.taskRunAttemptId,
          attempt
        );
      },
      tryCompleteBatchV3: async ({ payload }) => {
        await tryCompleteBatchV3(payload.batchId, prisma, payload.scheduleResumeOnComplete);
      },
      scheduleRequeueMessage: async ({ payload }) => {
        await marqs.requeueMessageById(payload.messageId);
      },
    },
  });

  if (env.LEGACY_RUN_ENGINE_WORKER_ENABLED === "true") {
    logger.debug(
      `👨‍🏭 Starting legacy run engine worker at host ${env.LEGACY_RUN_ENGINE_WORKER_REDIS_HOST}, pollInterval = ${env.LEGACY_RUN_ENGINE_WORKER_POLL_INTERVAL}, immediatePollInterval = ${env.LEGACY_RUN_ENGINE_WORKER_IMMEDIATE_POLL_INTERVAL}, workers = ${env.LEGACY_RUN_ENGINE_WORKER_CONCURRENCY_WORKERS}, tasksPerWorker = ${env.LEGACY_RUN_ENGINE_WORKER_CONCURRENCY_TASKS_PER_WORKER}, concurrencyLimit = ${env.LEGACY_RUN_ENGINE_WORKER_CONCURRENCY_LIMIT}`
    );

    worker.start();
  }

  return worker;
}

export const legacyRunEngineWorker = singleton("legacyRunEngineWorker", initializeWorker);
