import { z } from 'zod'
import { TRPCError } from '@trpc/server'
import { router, superAdminProcedure } from '../trpc'
import { logAudit } from '@/server/utils/audit'
import {
  generateWebhookSecret,
  deliverWebhook,
} from '@/server/services/webhook-dispatcher'

export const WEBHOOK_EVENTS = [
  'evaluation.submitted',
  'evaluation.updated',
  'project.created',
  'project.statusChanged',
  'round.activated',
  'round.closed',
  'stage.activated',
  'stage.closed',
  'assignment.created',
  'assignment.completed',
  'user.invited',
  'user.activated',
] as const

export const webhookRouter = router({
  /**
   * List all webhooks with delivery stats.
   */
  list: superAdminProcedure.query(async ({ ctx }) => {
    const webhooks = await ctx.prisma.webhook.findMany({
      include: {
        _count: {
          select: { deliveries: true },
        },
        createdBy: {
          select: { id: true, name: true, email: true },
        },
      },
      orderBy: { createdAt: 'desc' },
    })

    // Compute recent delivery stats for each webhook
    const now = new Date()
    const twentyFourHoursAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000)

    const stats = await Promise.all(
      webhooks.map(async (wh) => {
        const [delivered, failed] = await Promise.all([
          ctx.prisma.webhookDelivery.count({
            where: {
              webhookId: wh.id,
              status: 'DELIVERED',
              createdAt: { gte: twentyFourHoursAgo },
            },
          }),
          ctx.prisma.webhookDelivery.count({
            where: {
              webhookId: wh.id,
              status: 'FAILED',
              createdAt: { gte: twentyFourHoursAgo },
            },
          }),
        ])
        return {
          ...wh,
          recentDelivered: delivered,
          recentFailed: failed,
        }
      })
    )

    return stats
  }),

  /**
   * Create a new webhook.
   */
  create: superAdminProcedure
    .input(
      z.object({
        name: z.string().min(1).max(200),
        url: z.string().url(),
        events: z.array(z.string()).min(1),
        headers: z.record(z.string()).optional(),
        maxRetries: z.number().int().min(0).max(10).default(3),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const secret = generateWebhookSecret()

      const webhook = await ctx.prisma.webhook.create({
        data: {
          name: input.name,
          url: input.url,
          secret,
          events: input.events,
          headers: input.headers ?? undefined,
          maxRetries: input.maxRetries,
          createdById: ctx.user.id,
        },
      })

      try {
        await logAudit({
          prisma: ctx.prisma,
          userId: ctx.user.id,
          action: 'CREATE_WEBHOOK',
          entityType: 'Webhook',
          entityId: webhook.id,
          detailsJson: { name: input.name, url: input.url, events: input.events },
        })
      } catch (err) {
        console.error('[Webhook] Audit log failed:', err)
      }

      return webhook
    }),

  /**
   * Update a webhook.
   */
  update: superAdminProcedure
    .input(
      z.object({
        id: z.string(),
        name: z.string().min(1).max(200).optional(),
        url: z.string().url().optional(),
        events: z.array(z.string()).min(1).optional(),
        headers: z.record(z.string()).optional(),
        isActive: z.boolean().optional(),
        maxRetries: z.number().int().min(0).max(10).optional(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const { id, ...data } = input

      const webhook = await ctx.prisma.webhook.update({
        where: { id },
        data: {
          ...(data.name !== undefined ? { name: data.name } : {}),
          ...(data.url !== undefined ? { url: data.url } : {}),
          ...(data.events !== undefined ? { events: data.events } : {}),
          ...(data.headers !== undefined ? { headers: data.headers } : {}),
          ...(data.isActive !== undefined ? { isActive: data.isActive } : {}),
          ...(data.maxRetries !== undefined ? { maxRetries: data.maxRetries } : {}),
        },
      })

      try {
        await logAudit({
          prisma: ctx.prisma,
          userId: ctx.user.id,
          action: 'UPDATE_WEBHOOK',
          entityType: 'Webhook',
          entityId: id,
          detailsJson: { updatedFields: Object.keys(data) },
        })
      } catch (err) {
        console.error('[Webhook] Audit log failed:', err)
      }

      return webhook
    }),

  /**
   * Delete a webhook and its delivery history.
   */
  delete: superAdminProcedure
    .input(z.object({ id: z.string() }))
    .mutation(async ({ ctx, input }) => {
      // Cascade delete is defined in schema, so just delete the webhook
      await ctx.prisma.webhook.delete({
        where: { id: input.id },
      })

      try {
        await logAudit({
          prisma: ctx.prisma,
          userId: ctx.user.id,
          action: 'DELETE_WEBHOOK',
          entityType: 'Webhook',
          entityId: input.id,
        })
      } catch (err) {
        console.error('[Webhook] Audit log failed:', err)
      }

      return { success: true }
    }),

  /**
   * Send a test payload to a webhook.
   */
  test: superAdminProcedure
    .input(z.object({ id: z.string() }))
    .mutation(async ({ ctx, input }) => {
      const webhook = await ctx.prisma.webhook.findUnique({
        where: { id: input.id },
      })

      if (!webhook) {
        throw new TRPCError({ code: 'NOT_FOUND', message: 'Webhook not found' })
      }

      const testPayload = {
        event: 'test',
        timestamp: new Date().toISOString(),
        data: {
          message: 'This is a test webhook delivery from MOPC Platform.',
          webhookId: webhook.id,
          webhookName: webhook.name,
        },
      }

      const delivery = await ctx.prisma.webhookDelivery.create({
        data: {
          webhookId: webhook.id,
          event: 'test',
          payload: testPayload,
          status: 'PENDING',
          attempts: 0,
        },
      })

      await deliverWebhook(delivery.id)

      // Fetch updated delivery to get the result
      const result = await ctx.prisma.webhookDelivery.findUnique({
        where: { id: delivery.id },
      })

      try {
        await logAudit({
          prisma: ctx.prisma,
          userId: ctx.user.id,
          action: 'TEST_WEBHOOK',
          entityType: 'Webhook',
          entityId: input.id,
          detailsJson: { deliveryStatus: result?.status },
        })
      } catch (err) {
        console.error('[Webhook] Audit log failed:', err)
      }

      return result
    }),

  /**
   * Get paginated delivery log for a webhook.
   */
  getDeliveryLog: superAdminProcedure
    .input(
      z.object({
        webhookId: z.string(),
        page: z.number().int().min(1).default(1),
        pageSize: z.number().int().min(1).max(100).default(20),
      })
    )
    .query(async ({ ctx, input }) => {
      const skip = (input.page - 1) * input.pageSize

      const [items, total] = await Promise.all([
        ctx.prisma.webhookDelivery.findMany({
          where: { webhookId: input.webhookId },
          orderBy: { createdAt: 'desc' },
          skip,
          take: input.pageSize,
        }),
        ctx.prisma.webhookDelivery.count({
          where: { webhookId: input.webhookId },
        }),
      ])

      return {
        items,
        total,
        page: input.page,
        pageSize: input.pageSize,
        totalPages: Math.ceil(total / input.pageSize),
      }
    }),

  /**
   * Regenerate the HMAC secret for a webhook.
   */
  regenerateSecret: superAdminProcedure
    .input(z.object({ id: z.string() }))
    .mutation(async ({ ctx, input }) => {
      const newSecret = generateWebhookSecret()

      const webhook = await ctx.prisma.webhook.update({
        where: { id: input.id },
        data: { secret: newSecret },
      })

      try {
        await logAudit({
          prisma: ctx.prisma,
          userId: ctx.user.id,
          action: 'REGENERATE_WEBHOOK_SECRET',
          entityType: 'Webhook',
          entityId: input.id,
        })
      } catch (err) {
        console.error('[Webhook] Audit log failed:', err)
      }

      return webhook
    }),

  /**
   * Get available webhook events.
   */
  getAvailableEvents: superAdminProcedure.query(() => {
    return WEBHOOK_EVENTS
  }),
})
