import { Worker as RedisWorker } from "@trigger.dev/redis-worker";
import { z } from "zod";
import { env } from "~/env.server";
import { logger } from "~/services/logger.server";
import { singleton } from "~/utils/singleton";
import { ssoController } from "~/services/sso.server";
import { applyDirectorySyncEffects } from "~/services/directorySyncEffects.server";

// Dedicated worker for inbound account-management webhooks. The webhook
// proxy route verifies the signature via the plugin and enqueues the
// parsed event here; this worker calls back into the plugin to apply the
// DB writes. The plugin owns the vendor-specific logic; the webapp owns
// the queue runtime (this file), mirroring `commonWorker.server.ts`.
//
// Vendor-neutral by design: the catalog/job names and payload shape carry
// no provider identity.
const PayloadSchema = z
  .object({
    id: z.string(),
    event: z.string(),
    data: z.unknown(),
  })
  // Zod v3 treats a `z.unknown()` field as optional, so a payload missing
  // `data` entirely would otherwise validate. Require the key's presence
  // at runtime via refine — `.nonoptional()` is a v4-only API and isn't
  // available on the classic `z` import this repo pins.
  .refine((payload) => "data" in payload, {
    message: "data is required",
    path: ["data"],
  });

function initializeWorker() {
  const redisOptions = {
    keyPrefix: "accounts-webhook:worker:",
    host: env.COMMON_WORKER_REDIS_HOST,
    port: env.COMMON_WORKER_REDIS_PORT,
    username: env.COMMON_WORKER_REDIS_USERNAME,
    password: env.COMMON_WORKER_REDIS_PASSWORD,
    enableAutoPipelining: true,
    ...(env.COMMON_WORKER_REDIS_TLS_DISABLED === "true" ? {} : { tls: {} }),
  };

  const worker = new RedisWorker({
    name: "accounts-webhook-worker",
    redisOptions,
    catalog: {
      "account.webhook.event": {
        schema: PayloadSchema,
        visibilityTimeoutMs: 30_000,
        retry: { maxAttempts: 5 },
      },
    },
    concurrency: {
      workers: 2,
      tasksPerWorker: 4,
      limit: 8,
    },
    pollIntervalMs: 1_000,
    immediatePollIntervalMs: 50,
    shutdownTimeoutMs: 30_000,
    jobs: {
      "account.webhook.event": async ({ payload }) => {
        // The plugin returns a Result; throw on error so the worker
        // retries (a resolved err would otherwise be silently dropped).
        // `z.unknown()` infers `data?: unknown`, but the contract's
        // SsoWebhookEvent requires `data: unknown` — restate the fields
        // explicitly so the optional-vs-required shapes line up.
        const result = await ssoController.processWebhookEvent({
          id: payload.id,
          event: payload.event,
          data: payload.data,
        });
        if (result.isErr()) {
          throw new Error(`account webhook processing failed: ${result.error}`);
        }
        // Directory-sync events return membership effects to apply against
        // public.* tables (the plugin never writes those). A throw here
        // bubbles to the worker for retry; effects are idempotent.
        await applyDirectorySyncEffects(result.value.effects);
      },
    },
  });

  // Only poll on worker-role instances (same gate as commonWorker) and
  // only when the feature is enabled (no plugin loaded otherwise).
  if (env.COMMON_WORKER_ENABLED === "true" && env.SSO_ENABLED) {
    logger.debug(`👨‍🏭 Starting accounts webhook worker at host ${env.COMMON_WORKER_REDIS_HOST}`);
    worker.start();
  }

  return worker;
}

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