import { z } from "zod";
import { protectedProcedure, publicProcedure, router } from "../_core/trpc.ts";
import { getDb } from "../db.ts";
import { seasonalEvents, eventParticipation, socialStream, multiplierBoosts, streamInteractions } from "../../drizzle/schema.ts";
import { eq, gte, lte, and, desc } from "drizzle-orm";
import { TRPCError } from "@trpc/server";

export const retentionRouter = router({
  // ─── SEASONAL EVENTS ───────────────────────────────────────────────────────
  listEvents: publicProcedure
    .input(z.object({ status: z.enum(["upcoming", "active", "completed"]).optional(), limit: z.number().default(20) }))
    .query(async ({ input }) => {
      const db = await getDb();
      if (!db) throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Database unavailable" });

      const query = input.status
        ? db.select().from(seasonalEvents).where(eq(seasonalEvents.status, input.status)).limit(input.limit)
        : db.select().from(seasonalEvents).limit(input.limit);

      return query;
    }),

  getEventDetails: publicProcedure
    .input(z.object({ eventId: z.number() }))
    .query(async ({ input }) => {
      const db = await getDb();
      if (!db) throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Database unavailable" });

      const event = await db.select().from(seasonalEvents).where(eq(seasonalEvents.id, input.eventId)).limit(1);
      if (!event.length) throw new TRPCError({ code: "NOT_FOUND", message: "Event not found" });

      const participants = await db
        .select()
        .from(eventParticipation)
        .where(eq(eventParticipation.eventId, input.eventId))
        .orderBy(desc(eventParticipation.score))
        .limit(100);

      return { event: event[0], participants };
    }),

  createEvent: protectedProcedure
    .input(
      z.object({
        name: z.string(),
        description: z.string().optional(),
        type: z.enum(["tournament", "promotion", "special_game", "bonus_multiplier"]),
        startAt: z.date(),
        endAt: z.date(),
        rewardType: z.enum(["gc", "sc", "multiplier", "exclusive_access"]),
        rewardAmount: z.number().optional(),
        maxParticipants: z.number().optional(),
        bannerUrl: z.string().optional(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      if (ctx.user.role !== "admin") throw new TRPCError({ code: "FORBIDDEN" });

      const db = await getDb();
      if (!db) throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Database unavailable" });

      const result = await db.insert(seasonalEvents).values({
        name: input.name,
        description: input.description,
        type: input.type,
        startAt: input.startAt,
        endAt: input.endAt,
        rewardType: input.rewardType,
        rewardAmount: input.rewardAmount || 0,
        maxParticipants: input.maxParticipants,
        bannerUrl: input.bannerUrl,
        createdBy: ctx.user.id,
      });

      return { success: true, eventId: result[0].insertId };
    }),

  joinEvent: protectedProcedure
    .input(z.object({ eventId: z.number() }))
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Database unavailable" });

      const existing = await db
        .select()
        .from(eventParticipation)
        .where(and(eq(eventParticipation.eventId, input.eventId), eq(eventParticipation.userId, ctx.user.id)))
        .limit(1);

      if (existing.length) throw new TRPCError({ code: "BAD_REQUEST", message: "Already joined this event" });

      await db.insert(eventParticipation).values({ eventId: input.eventId, userId: ctx.user.id });

      return { success: true };
    }),

  // ─── SOCIAL STREAM ───────────────────────────────────────────────────────
  getFeed: protectedProcedure
    .input(z.object({ limit: z.number().default(20), offset: z.number().default(0) }))
    .query(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Database unavailable" });

      const entries = await db
        .select()
        .from(socialStream)
        .where(eq(socialStream.visibility, "public"))
        .orderBy(desc(socialStream.createdAt))
        .limit(input.limit)
        .offset(input.offset);

      return entries;
    }),

  getUserFeed: protectedProcedure
    .input(z.object({ userId: z.number(), limit: z.number().default(20) }))
    .query(async ({ input }) => {
      const db = await getDb();
      if (!db) throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Database unavailable" });

      return db
        .select()
        .from(socialStream)
        .where(eq(socialStream.userId, input.userId))
        .orderBy(desc(socialStream.createdAt))
        .limit(input.limit);
    }),

  createStreamEntry: protectedProcedure
    .input(
      z.object({
        activityType: z.enum(["win", "achievement_unlocked", "tournament_joined", "vip_tier_upgrade", "milestone_reached", "friend_added"]),
        title: z.string(),
        description: z.string().optional(),
        metadata: z.record(z.string(), z.any()).optional(),
        visibility: z.enum(["public", "friends_only", "private"]).default("public"),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Database unavailable" });

      const result = await db.insert(socialStream).values({
        userId: ctx.user.id,
        activityType: input.activityType,
        title: input.title,
        description: input.description,
        metadata: input.metadata,
        visibility: input.visibility,
      });

      return { success: true, entryId: result[0].insertId };
    }),

  likeStreamEntry: protectedProcedure
    .input(z.object({ entryId: z.number() }))
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Database unavailable" });

      const existing = await db
        .select()
        .from(streamInteractions)
        .where(
          and(
            eq(streamInteractions.streamEntryId, input.entryId),
            eq(streamInteractions.userId, ctx.user.id),
            eq(streamInteractions.interactionType, "like")
          )
        )
        .limit(1);

      if (existing.length) throw new TRPCError({ code: "BAD_REQUEST", message: "Already liked this entry" });

      await db.insert(streamInteractions).values({
        streamEntryId: input.entryId,
        userId: ctx.user.id,
        interactionType: "like",
      });

      const entry = await db.select().from(socialStream).where(eq(socialStream.id, input.entryId)).limit(1);
      if (entry.length) {
        await db
          .update(socialStream)
          .set({ likeCount: (entry[0].likeCount || 0) + 1 })
          .where(eq(socialStream.id, input.entryId));
      }

      return { success: true };
    }),

  commentStreamEntry: protectedProcedure
    .input(z.object({ entryId: z.number(), content: z.string() }))
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Database unavailable" });

      await db.insert(streamInteractions).values({
        streamEntryId: input.entryId,
        userId: ctx.user.id,
        interactionType: "comment",
        content: input.content,
      });

      const entry = await db.select().from(socialStream).where(eq(socialStream.id, input.entryId)).limit(1);
      if (entry.length) {
        await db
          .update(socialStream)
          .set({ commentCount: (entry[0].commentCount || 0) + 1 })
          .where(eq(socialStream.id, input.entryId));
      }

      return { success: true };
    }),

  // ─── MULTIPLIER BOOSTS ───────────────────────────────────────────────────
  getActiveMultipliers: protectedProcedure.query(async ({ ctx }) => {
    const db = await getDb();
    if (!db) throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Database unavailable" });

    const now = new Date();
    return db
      .select()
      .from(multiplierBoosts)
      .where(
        and(
          eq(multiplierBoosts.userId, ctx.user.id),
          eq(multiplierBoosts.isActive, true),
          gte(multiplierBoosts.endAt, now)
        )
      );
  }),

  createMultiplier: protectedProcedure
    .input(
      z.object({
        userId: z.number(),
        multiplier: z.number().min(1).max(10),
        reason: z.enum(["event", "achievement", "vip_tier", "promotional", "referral"]),
        startAt: z.date(),
        endAt: z.date(),
        appliedGames: z.array(z.number()).optional(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      if (ctx.user.role !== "admin") throw new TRPCError({ code: "FORBIDDEN" });

      const db = await getDb();
      if (!db) throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Database unavailable" });

      const result = await db.insert(multiplierBoosts).values({
        userId: input.userId,
        multiplier: input.multiplier.toString() as any,
        reason: input.reason,
        startAt: input.startAt,
        endAt: input.endAt,
        appliedGames: input.appliedGames ? (JSON.stringify(input.appliedGames) as any) : null,
      });

      return { success: true, boostId: result[0].insertId };
    }),

  getMultiplierStats: protectedProcedure.query(async ({ ctx }) => {
    const db = await getDb();
    if (!db) throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Database unavailable" });

    const boosts = await db
      .select()
      .from(multiplierBoosts)
      .where(eq(multiplierBoosts.userId, ctx.user.id));

    const now = new Date();
    const active = boosts.filter((b) => b.isActive && new Date(b.endAt) > now);
    const maxMultiplier = active.length > 0 ? Math.max(...active.map((b) => typeof b.multiplier === 'string' ? parseFloat(b.multiplier) : b.multiplier)) : 1;

    return {
      activeBoosts: active.length,
      maxMultiplier,
      boosts: boosts.slice(0, 10),
    };
  }),
});
