import { z } from "zod";
import { createAuditEntry } from "../lib/audit.js";
import { assertWebhookUrlAllowed } from "../lib/ssrf-guard.js";
import type { TRPCContext } from "../trpc.js";
import {
  buildWebhookCreateData,
  buildWebhookUpdateData,
  createWebhookInputSchema,
  loadWebhookOrThrow,
  sendWebhookTestRequest,
  updateWebhookInputSchema,
} from "./webhook-support.js";

export const WebhookIdInputSchema = z.object({
  id: z.string(),
});

export const CreateWebhookInputSchema = createWebhookInputSchema;

export const UpdateWebhookProcedureInputSchema = z.object({
  id: z.string(),
  data: updateWebhookInputSchema,
});

type WebhookProcedureContext = Pick<TRPCContext, "db" | "dbUser">;

function withAuditUser(userId: string | undefined) {
  return userId ? { userId } : {};
}

export async function listWebhooks(ctx: WebhookProcedureContext) {
  return ctx.db.webhook.findMany({
    orderBy: { createdAt: "desc" },
  });
}

export async function getWebhookById(
  ctx: WebhookProcedureContext,
  input: z.infer<typeof WebhookIdInputSchema>,
) {
  return loadWebhookOrThrow(ctx.db, input.id);
}

export async function createWebhook(
  ctx: WebhookProcedureContext,
  input: z.infer<typeof CreateWebhookInputSchema>,
) {
  await assertWebhookUrlAllowed(input.url);

  const webhook = await ctx.db.webhook.create({
    data: buildWebhookCreateData(input),
  });

  void createAuditEntry({
    db: ctx.db,
    entityType: "Webhook",
    entityId: webhook.id,
    entityName: webhook.name,
    action: "CREATE",
    ...withAuditUser(ctx.dbUser?.id),
    after: webhook as unknown as Record<string, unknown>,
    source: "ui",
  });

  return webhook;
}

export async function updateWebhook(
  ctx: WebhookProcedureContext,
  input: z.infer<typeof UpdateWebhookProcedureInputSchema>,
) {
  if (input.data.url !== undefined) {
    await assertWebhookUrlAllowed(input.data.url);
  }

  const existing = await loadWebhookOrThrow(ctx.db, input.id);

  const updated = await ctx.db.webhook.update({
    where: { id: input.id },
    data: buildWebhookUpdateData(input.data),
  });

  void createAuditEntry({
    db: ctx.db,
    entityType: "Webhook",
    entityId: input.id,
    entityName: updated.name,
    action: "UPDATE",
    ...withAuditUser(ctx.dbUser?.id),
    before: existing as unknown as Record<string, unknown>,
    after: updated as unknown as Record<string, unknown>,
    source: "ui",
  });

  return updated;
}

export async function deleteWebhook(
  ctx: WebhookProcedureContext,
  input: z.infer<typeof WebhookIdInputSchema>,
) {
  const existing = await loadWebhookOrThrow(ctx.db, input.id);
  await ctx.db.webhook.delete({ where: { id: input.id } });

  void createAuditEntry({
    db: ctx.db,
    entityType: "Webhook",
    entityId: input.id,
    entityName: existing.name,
    action: "DELETE",
    ...withAuditUser(ctx.dbUser?.id),
    before: existing as unknown as Record<string, unknown>,
    source: "ui",
  });
}

export async function testWebhook(
  ctx: WebhookProcedureContext,
  input: z.infer<typeof WebhookIdInputSchema>,
) {
  const webhook = await loadWebhookOrThrow(ctx.db, input.id);
  await assertWebhookUrlAllowed(webhook.url);
  const result = await sendWebhookTestRequest(webhook);

  void createAuditEntry({
    db: ctx.db,
    entityType: "Webhook",
    entityId: webhook.id,
    entityName: webhook.name,
    action: "UPDATE",
    ...withAuditUser(ctx.dbUser?.id),
    summary: `Tested webhook (result: ${result.success ? "success" : "failed"})`,
    metadata: result as unknown as Record<string, unknown>,
    source: "ui",
  });

  return result;
}
