import { protectedProcedure, router } from "../_core/trpc.ts";
import { TRPCError } from "@trpc/server";
import { getDb, writeAuditLog } from "../db.ts";
import { eq, and, desc, sql } from "drizzle-orm";
import z from "zod";

export const customNotificationsRouter = router({
  /**
   * Send a custom notification to a user
   */
  send: protectedProcedure
    .input(
      z.object({
        userId: z.number().optional(),
        title: z.string().min(1).max(200),
        message: z.string().max(1000),
        type: z.enum([
          "payment_success", "payment_failed", "bonus_credited", "game_win", "game_loss",
          "bet_placed", "bet_settled", "odds_changed", "poker_table_joined", "poker_hand_result",
          "poker_tournament_update", "bingo_card_purchased", "bingo_number_called", "bingo_win",
          "kyc_approved", "kyc_rejected", "kyc_pending", "account_suspended", "account_restored",
          "withdrawal_approved", "withdrawal_rejected", "system_alert", "promotional", "admin_broadcast"
        ]).default("system_alert"),
        channels: z.array(z.enum(["in_app", "email", "push"])).optional(),
        data: z.record(z.string(), z.any()).optional(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      const schema = await import("../../drizzle/schema");

      // Only allow admins to send to other users
      const targetUserId = input.userId || ctx.user.id;
      if (input.userId && input.userId !== ctx.user.id && ctx.user.role !== "admin") {
        throw new TRPCError({ code: "FORBIDDEN", message: "Cannot send notifications to other users" });
      }

      try {
        await db.insert(schema.notifications).values({
          userId: targetUserId,
          title: input.title,
          message: input.message,
          type: input.type,
          channels: input.channels || ["in_app"],
          data: input.data || {},
          isRead: false,
          createdAt: new Date(),
        });

        // Log the action
        await writeAuditLog({
          actorId: ctx.user.id,
          action: "notification_sent",
          category: "system",
          details: {
            targetUserId,
            title: input.title,
            type: input.type,
          },
        });

        return { success: true, notificationId: 0 };
      } catch (error) {
        console.error("Failed to send notification:", error);
        throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Failed to send notification" });
      }
    }),

  /**
   * Send bulk notifications to multiple users
   */
  sendBulk: protectedProcedure
    .input(
      z.object({
        userIds: z.array(z.number()).min(1),
        title: z.string().min(1).max(200),
        message: z.string().max(1000),
        type: z.enum([
          "payment_success", "payment_failed", "bonus_credited", "game_win", "game_loss",
          "bet_placed", "bet_settled", "odds_changed", "poker_table_joined", "poker_hand_result",
          "poker_tournament_update", "bingo_card_purchased", "bingo_number_called", "bingo_win",
          "kyc_approved", "kyc_rejected", "kyc_pending", "account_suspended", "account_restored",
          "withdrawal_approved", "withdrawal_rejected", "system_alert", "promotional", "admin_broadcast"
        ]).default("system_alert"),
        channels: z.array(z.enum(["in_app", "email", "push"])).optional(),
        data: z.record(z.string(), z.any()).optional(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Only admins can send bulk notifications
      if (ctx.user.role !== "admin") {
        throw new TRPCError({ code: "FORBIDDEN", message: "Only admins can send bulk notifications" });
      }

      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      const schema = await import("../../drizzle/schema");

      try {
        const notifications = input.userIds.map((userId) => ({
          userId,
          title: input.title,
          message: input.message,
          type: input.type,
          channels: input.channels || ["in_app"],
          data: input.data || {},
          isRead: false,
          createdAt: new Date(),
        }));

        await db.insert(schema.notifications).values(notifications);

        // Log the action
        await writeAuditLog({
          actorId: ctx.user.id,
          action: "bulk_notifications_sent",
          category: "system",
          details: {
            count: input.userIds.length,
            title: input.title,
            type: input.type,
          },
        });

        return { success: true, count: input.userIds.length };
      } catch (error) {
        console.error("Failed to send bulk notifications:", error);
        throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Failed to send bulk notifications" });
      }
    }),

  /**
   * Get notifications with filters
   */
  getNotifications: protectedProcedure
    .input(
      z.object({
        limit: z.number().default(20),
        offset: z.number().default(0),
        unreadOnly: z.boolean().default(false),
      })
    )
    .query(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) return [];

      const schema = await import("../../drizzle/schema");

      const conditions = [eq(schema.notifications.userId, ctx.user.id)];

      if (input.unreadOnly) {
        conditions.push(eq(schema.notifications.isRead, false));
      }

      try {
        const result = await db
          .select()
          .from(schema.notifications)
          .where(and(...conditions))
          .orderBy(desc(schema.notifications.createdAt))
          .limit(input.limit)
          .offset(input.offset);

        return result;
      } catch (error) {
        console.error("Failed to get notifications:", error);
        return [];
      }
    }),

  /**
   * Get notification stats
   */
  getStats: protectedProcedure.query(async ({ ctx }) => {
    const db = await getDb();
    if (!db) return { total: 0, unread: 0, byType: {} };

    const schema = await import("../../drizzle/schema");

    try {
      const total = await db
        .select({ count: sql`COUNT(*)`.mapWith(Number) })
        .from(schema.notifications)
        .where(eq(schema.notifications.userId, ctx.user.id));

      const unread = await db
        .select({ count: sql`COUNT(*)`.mapWith(Number) })
        .from(schema.notifications)
        .where(and(eq(schema.notifications.userId, ctx.user.id), eq(schema.notifications.isRead, false)));

      const byType = await db
        .select({
          type: schema.notifications.type,
          count: sql`COUNT(*)`.mapWith(Number),
        })
        .from(schema.notifications)
        .where(eq(schema.notifications.userId, ctx.user.id))
        .groupBy((row) => row.type);

      return {
        total: total[0]?.count || 0,
        unread: unread[0]?.count || 0,
        byType: Object.fromEntries(byType.map((t) => [t.type, t.count])),
      };
    } catch (error) {
      console.error("Failed to get notification stats:", error);
      return { total: 0, unread: 0, byType: {} };
    }
  }),

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

      const schema = await import("../../drizzle/schema");

      const notif = await db
        .select()
        .from(schema.notifications)
        .where(eq(schema.notifications.id, input.notificationId))
        .limit(1);

      if (!notif[0] || notif[0].userId !== ctx.user.id) {
        throw new TRPCError({ code: "FORBIDDEN", message: "Not your notification" });
      }

      await db
        .update(schema.notifications)
        .set({ isRead: true, readAt: new Date() })
        .where(eq(schema.notifications.id, input.notificationId));

      return { success: true };
    }),

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

    const schema = await import("../../drizzle/schema");

    await db
      .update(schema.notifications)
      .set({ isRead: true, readAt: new Date() })
      .where(and(eq(schema.notifications.userId, ctx.user.id), eq(schema.notifications.isRead, false)));

    return { success: true };
  }),

  /**
   * Delete notification
   */
  deleteNotification: protectedProcedure
    .input(z.object({ notificationId: z.number() }))
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      const schema = await import("../../drizzle/schema");

      const notif = await db
        .select()
        .from(schema.notifications)
        .where(eq(schema.notifications.id, input.notificationId))
        .limit(1);

      if (!notif[0] || notif[0].userId !== ctx.user.id) {
        throw new TRPCError({ code: "FORBIDDEN", message: "Not your notification" });
      }

      await db.delete(schema.notifications).where(eq(schema.notifications.id, input.notificationId));

      return { success: true };
    }),

  /**
   * Clear all notifications
   */
  clearAll: protectedProcedure.mutation(async ({ ctx }) => {
    const db = await getDb();
    if (!db) throw new Error("Database unavailable");

    const schema = await import("../../drizzle/schema");

    await db.delete(schema.notifications).where(eq(schema.notifications.userId, ctx.user.id));

    return { success: true };
  }),
});
