import { protectedProcedure, router } from "../_core/trpc.ts";
import { TRPCError } from "@trpc/server";
import { z } from "zod";
import { getDb } from "../db.ts";
import { referrals, wallets } from "../../drizzle/schema.ts";
import { eq } from "drizzle-orm";
import { nanoid } from "nanoid";

export const referralsRouter = router({
  generateReferralLink: protectedProcedure
    .input(z.object({ source: z.enum(["facebook", "messenger", "sms", "twitter", "instagram", "email", "other"]) }))
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new TRPCError({ code: "INTERNAL_SERVER_ERROR" });

      const code = nanoid(12);
      const link = `${process.env.VITE_APP_URL || "https://playplaycoinkrazy.com"}?ref=${code}`;

      await db.insert(referrals).values({
        referrerId: ctx.user!.id,
        refereeId: 0,
        referralCode: code,
        referralLink: link,
        source: input.source,
        status: "pending",
        rewardAmount: 1,
        createdAt: new Date(),
      } as any);

      return { code, link };
    }),

  completeReferral: protectedProcedure
    .input(z.object({ referralCode: z.string() }))
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new TRPCError({ code: "INTERNAL_SERVER_ERROR" });

      const ref = await db.select().from(referrals).where(eq(referrals.referralCode, input.referralCode)).limit(1);
      if (!ref[0]) throw new TRPCError({ code: "NOT_FOUND" });

      await db
        .update(referrals)
        .set({ refereeId: ctx.user!.id, status: "completed", completedAt: new Date() })
        .where(eq(referrals.referralCode, input.referralCode));

      // Credit both users 1 SC
      const wallet = await db.select().from(wallets).where(eq(wallets.userId, ctx.user!.id)).limit(1);
      if (wallet[0]) {
        const newBalance = (Number(wallet[0].scBalance) || 0) + 1;
        await db
          .update(wallets)
          .set({ scBalance: newBalance.toString() })
          .where(eq(wallets.userId, ctx.user!.id));
      }

      return { success: true };
    }),

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

    const refs = await db.select().from(referrals).where(eq(referrals.referrerId, ctx.user!.id));
    const completed = refs.filter((r) => r.status === "completed").length;
    const pending = refs.filter((r) => r.status === "pending").length;
    const totalRewards = completed * 1; // 1 SC per referral

    return {
      totalReferrals: refs.length,
      completedReferrals: completed,
      pendingReferrals: pending,
      totalRewards,
      recentReferrals: refs.slice(-5).reverse(),
    };
  }),

  getTopReferrers: protectedProcedure
    .input(z.object({ limit: z.number().default(10), period: z.enum(["all_time", "monthly", "weekly"]).default("all_time") }))
    .query(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new TRPCError({ code: "INTERNAL_SERVER_ERROR" });

      const allReferrals = await db.select().from(referrals);

      // Filter by period
      const now = new Date();
      let filteredReferrals = allReferrals;

      if (input.period === "monthly") {
        const monthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
        filteredReferrals = allReferrals.filter((r) => new Date(r.createdAt!).getTime() >= monthAgo.getTime());
      } else if (input.period === "weekly") {
        const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
        filteredReferrals = allReferrals.filter((r) => new Date(r.createdAt!).getTime() >= weekAgo.getTime());
      }

      // Group by referrer and count completed referrals
      const referrerStats: Record<number, { completedCount: number; totalReward: number; referrerId: number }> = {};

      filteredReferrals.forEach((ref) => {
        if (!referrerStats[ref.referrerId]) {
          referrerStats[ref.referrerId] = { completedCount: 0, totalReward: 0, referrerId: ref.referrerId };
        }
        if (ref.status === "completed") {
          referrerStats[ref.referrerId].completedCount += 1;
          referrerStats[ref.referrerId].totalReward += ref.rewardAmount || 1;
        }
      });

      // Sort by total reward and limit
      const topReferrers = Object.values(referrerStats)
        .sort((a, b) => b.totalReward - a.totalReward)
        .slice(0, input.limit);

      return topReferrers.map((ref, index) => ({
        rank: index + 1,
        referrerId: ref.referrerId,
        completedReferrals: ref.completedCount,
        totalRewards: ref.totalReward,
      }));
    }),

  getUserReferralRank: protectedProcedure
    .input(z.object({ period: z.enum(["all_time", "monthly", "weekly"]).default("all_time") }))
    .query(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new TRPCError({ code: "INTERNAL_SERVER_ERROR" });

      const allReferrals = await db.select().from(referrals);

      // Filter by period
      const now = new Date();
      let filteredReferrals = allReferrals;

      if (input.period === "monthly") {
        const monthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
        filteredReferrals = allReferrals.filter((r) => new Date(r.createdAt!).getTime() >= monthAgo.getTime());
      } else if (input.period === "weekly") {
        const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
        filteredReferrals = allReferrals.filter((r) => new Date(r.createdAt!).getTime() >= weekAgo.getTime());
      }

      // Group by referrer
      const referrerStats: Record<number, { completedCount: number; totalReward: number; referrerId: number }> = {};

      filteredReferrals.forEach((ref) => {
        if (!referrerStats[ref.referrerId]) {
          referrerStats[ref.referrerId] = { completedCount: 0, totalReward: 0, referrerId: ref.referrerId };
        }
        if (ref.status === "completed") {
          referrerStats[ref.referrerId].completedCount += 1;
          referrerStats[ref.referrerId].totalReward += ref.rewardAmount || 1;
        }
      });

      // Sort and find user's rank
      const sortedReferrers = Object.values(referrerStats).sort((a, b) => b.totalReward - a.totalReward);

      const userRank = sortedReferrers.findIndex((ref) => ref.referrerId === ctx.user!.id) + 1;
      const userStats = referrerStats[ctx.user!.id];

      return {
        rank: userRank > 0 ? userRank : null,
        completedReferrals: userStats?.completedCount || 0,
        totalRewards: userStats?.totalReward || 0,
        totalReferrers: sortedReferrers.length,
      };
    }),

  getReferralHistory: 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" });

      const refs = await db
        .select()
        .from(referrals)
        .where(eq(referrals.referrerId, ctx.user!.id));

      return refs
        .slice(input.offset, input.offset + input.limit)
        .map((ref) => ({
          id: ref.id,
          code: ref.referralCode,
          source: ref.source,
          status: ref.status,
          reward: ref.rewardAmount || 1,
          createdAt: ref.createdAt,
          completedAt: ref.completedAt,
        }));
    }),
});
