import { router, adminProcedure } from "../_core/trpc.ts";
import { z } from "zod";
import { getDb } from "../db.ts";
import { adminNotifications, webhookConfigs, webhookDeliveries } from "../../drizzle/schema.ts";
import { eq, desc, and, gte, lte } from "drizzle-orm";

export const adminNotificationsRouter = router({
  /**
   * List admin notifications with pagination
   */
  listNotifications: adminProcedure
    .input(
      z.object({
        page: z.number().min(1).default(1),
        limit: z.number().min(1).max(100).default(20),
        type: z.string().optional(),
        severity: z.enum(['low', 'medium', 'high', 'critical']).optional(),
        unreadOnly: z.boolean().default(false),
      })
    )
    .query(async ({ input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      const offset = (input.page - 1) * input.limit;
      const conditions = [];

      if (input.unreadOnly) {
        conditions.push(eq(adminNotifications.isRead, 0));
      }
      if (input.type) {
        conditions.push(eq(adminNotifications.type, input.type as any));
      }
      if (input.severity) {
        conditions.push(eq(adminNotifications.severity, input.severity));
      }

      const whereClause = conditions.length > 0 ? and(...conditions) : undefined;

      const [notifications, countResult] = await Promise.all([
        db
          .select()
          .from(adminNotifications)
          .where(whereClause)
          .orderBy(desc(adminNotifications.createdAt))
          .limit(input.limit)
          .offset(offset),
        db
          .select({ count: adminNotifications.id })
          .from(adminNotifications)
          .where(whereClause),
      ]);

      const total = countResult[0]?.count || 0;

      return {
        notifications,
        total,
        pages: Math.ceil(total / input.limit),
        page: input.page,
      };
    }),

  /**
   * Mark notification as read
   */
  markAsRead: adminProcedure
    .input(z.object({ id: z.number() }))
    .mutation(async ({ input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      await db
        .update(adminNotifications)
        .set({ isRead: 1, readAt: new Date() })
        .where(eq(adminNotifications.id, input.id));

      return { success: true };
    }),

  /**
   * Mark all notifications as read
   */
  markAllAsRead: adminProcedure.mutation(async () => {
    const db = await getDb();
    if (!db) throw new Error("Database unavailable");

    await db
      .update(adminNotifications)
      .set({ isRead: 1, readAt: new Date() })
      .where(eq(adminNotifications.isRead, 0));

    return { success: true };
  }),

  /**
   * Get unread notification count
   */
  getUnreadCount: adminProcedure.query(async () => {
    const db = await getDb();
    if (!db) throw new Error("Database unavailable");

    const result = await db
      .select({ count: adminNotifications.id })
      .from(adminNotifications)
      .where(eq(adminNotifications.isRead, 0));

    return result[0]?.count || 0;
  }),

  /**
   * Manage webhook configurations
   */
  listWebhooks: adminProcedure.query(async () => {
    const db = await getDb();
    if (!db) throw new Error("Database unavailable");

    return await db.select().from(webhookConfigs).orderBy(desc(webhookConfigs.createdAt));
  }),

  /**
   * Create webhook configuration
   */
  createWebhook: adminProcedure
    .input(
      z.object({
        name: z.string().min(1),
        url: z.string().url(),
        events: z.array(z.string()).min(1),
        headers: z.record(z.string()).optional(),
        retryCount: z.number().min(0).max(10).default(3),
        retryDelay: z.number().min(60).default(300),
        timeout: z.number().min(5).max(300).default(30),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      const secret = Math.random().toString(36).substring(2, 15);

      const result = await db.insert(webhookConfigs).values({
        name: input.name,
        url: input.url,
        events: input.events,
        headers: input.headers || {},
        secret,
        retryCount: input.retryCount,
        retryDelay: input.retryDelay,
        timeout: input.timeout,
        createdBy: ctx.user.id,
      });

      return {
        success: true,
        id: result.insertId,
        secret,
      };
    }),

  /**
   * Update webhook configuration
   */
  updateWebhook: adminProcedure
    .input(
      z.object({
        id: z.number(),
        name: z.string().optional(),
        url: z.string().url().optional(),
        events: z.array(z.string()).optional(),
        isActive: z.number().optional(),
        retryCount: z.number().optional(),
        retryDelay: z.number().optional(),
        timeout: z.number().optional(),
      })
    )
    .mutation(async ({ input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      const updates: any = {};
      if (input.name) updates.name = input.name;
      if (input.url) updates.url = input.url;
      if (input.events) updates.events = input.events;
      if (input.isActive !== undefined) updates.isActive = input.isActive;
      if (input.retryCount !== undefined) updates.retryCount = input.retryCount;
      if (input.retryDelay !== undefined) updates.retryDelay = input.retryDelay;
      if (input.timeout !== undefined) updates.timeout = input.timeout;

      await db
        .update(webhookConfigs)
        .set(updates)
        .where(eq(webhookConfigs.id, input.id));

      return { success: true };
    }),

  /**
   * Delete webhook configuration
   */
  deleteWebhook: adminProcedure
    .input(z.object({ id: z.number() }))
    .mutation(async ({ input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      await db.delete(webhookConfigs).where(eq(webhookConfigs.id, input.id));

      return { success: true };
    }),

  /**
   * Get webhook delivery history
   */
  getWebhookDeliveries: adminProcedure
    .input(
      z.object({
        webhookConfigId: z.number(),
        page: z.number().min(1).default(1),
        limit: z.number().min(1).max(100).default(20),
      })
    )
    .query(async ({ input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      const offset = (input.page - 1) * input.limit;

      const [deliveries, countResult] = await Promise.all([
        db
          .select()
          .from(webhookDeliveries)
          .where(eq(webhookDeliveries.webhookConfigId, input.webhookConfigId))
          .orderBy(desc(webhookDeliveries.createdAt))
          .limit(input.limit)
          .offset(offset),
        db
          .select({ count: webhookDeliveries.id })
          .from(webhookDeliveries)
          .where(eq(webhookDeliveries.webhookConfigId, input.webhookConfigId)),
      ]);

      const total = countResult[0]?.count || 0;

      return {
        deliveries,
        total,
        pages: Math.ceil(total / input.limit),
      };
    }),

  /**
   * Get notification statistics
   */
  getStats: adminProcedure.query(async () => {
    const db = await getDb();
    if (!db) throw new Error("Database unavailable");

    const now = new Date();
    const last24h = new Date(now.getTime() - 24 * 60 * 60 * 1000);
    const last7d = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);

    const [total, unread, last24hCount, last7dCount, byType, bySeverity] = await Promise.all([
      db.select({ count: adminNotifications.id }).from(adminNotifications),
      db
        .select({ count: adminNotifications.id })
        .from(adminNotifications)
        .where(eq(adminNotifications.isRead, 0)),
      db
        .select({ count: adminNotifications.id })
        .from(adminNotifications)
        .where(gte(adminNotifications.createdAt, last24h)),
      db
        .select({ count: adminNotifications.id })
        .from(adminNotifications)
        .where(gte(adminNotifications.createdAt, last7d)),
      db
        .select({
          type: adminNotifications.type,
          count: adminNotifications.id,
        })
        .from(adminNotifications),
      db
        .select({
          severity: adminNotifications.severity,
          count: adminNotifications.id,
        })
        .from(adminNotifications),
    ]);

    return {
      total: total[0]?.count || 0,
      unread: unread[0]?.count || 0,
      last24h: last24hCount[0]?.count || 0,
      last7d: last7dCount[0]?.count || 0,
      byType: byType.reduce((acc: any, row: any) => {
        acc[row.type] = row.count;
        return acc;
      }, {}),
      bySeverity: bySeverity.reduce((acc: any, row: any) => {
        acc[row.severity] = row.count;
        return acc;
      }, {}),
    };
  }),
});
