import { describe, it, expect, beforeAll, afterAll } from "vitest";
import { getDb } from "../db.ts";
import { referrals, wallets, users } from "../../drizzle/schema.ts";
import { eq } from "drizzle-orm";

describe("Referrals System", () => {
  let db: any;

  beforeAll(async () => {
    db = await getDb();
  });

  afterAll(async () => {
    // Cleanup test data
    if (db) {
      await db.delete(referrals).where(eq(referrals.referralCode, "TEST_CODE"));
    }
  });

  describe("Referral Stats", () => {
    it("should calculate total referrals correctly", async () => {
      // Create test referrals
      const testCode = "TEST_CODE_" + Date.now();
      await db.insert(referrals).values({
        referrerId: 1,
        refereeId: 0,
        referralCode: testCode,
        referralLink: `https://playplaycoinkrazy.com?ref=${testCode}`,
        source: "facebook",
        status: "pending",
        rewardAmount: 1,
        createdAt: new Date(),
      } as any);

      const refs = await db.select().from(referrals).where(eq(referrals.referrerId, 1));
      expect(refs.length).toBeGreaterThan(0);
    });

    it("should track completed referrals", async () => {
      const testCode = "COMPLETED_" + Date.now();
      await db.insert(referrals).values({
        referrerId: 1,
        refereeId: 2,
        referralCode: testCode,
        referralLink: `https://playplaycoinkrazy.com?ref=${testCode}`,
        source: "email",
        status: "completed",
        rewardAmount: 1,
        createdAt: new Date(),
        completedAt: new Date(),
      } as any);

      const completed = await db
        .select()
        .from(referrals)
        .where(eq(referrals.status, "completed"));
      expect(completed.length).toBeGreaterThan(0);
    });

    it("should calculate pending referrals", async () => {
      const testCode = "PENDING_" + Date.now();
      await db.insert(referrals).values({
        referrerId: 1,
        refereeId: 0,
        referralCode: testCode,
        referralLink: `https://playplaycoinkrazy.com?ref=${testCode}`,
        source: "twitter",
        status: "pending",
        rewardAmount: 1,
        createdAt: new Date(),
      } as any);

      const pending = await db
        .select()
        .from(referrals)
        .where(eq(referrals.status, "pending"));
      expect(pending.length).toBeGreaterThan(0);
    });
  });

  describe("Referral Leaderboard", () => {
    it("should rank referrers by total rewards", async () => {
      const allReferrals = await db.select().from(referrals);

      const referrerStats: Record<
        number,
        { completedCount: number; totalReward: number; referrerId: number }
      > = {};

      allReferrals.forEach((ref: any) => {
        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;
        }
      });

      const topReferrers = Object.values(referrerStats)
        .sort((a, b) => b.totalReward - a.totalReward)
        .slice(0, 10);

      expect(Array.isArray(topReferrers)).toBe(true);
      if (topReferrers.length > 1) {
        expect(topReferrers[0].totalReward).toBeGreaterThanOrEqual(
          topReferrers[1].totalReward
        );
      }
    });

    it("should filter referrers by period", async () => {
      const allReferrals = await db.select().from(referrals);
      const now = new Date();
      const monthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);

      const monthlyReferrals = allReferrals.filter(
        (r: any) => new Date(r.createdAt!).getTime() >= monthAgo.getTime()
      );

      expect(Array.isArray(monthlyReferrals)).toBe(true);
      expect(monthlyReferrals.length).toBeLessThanOrEqual(allReferrals.length);
    });
  });

  describe("Referral History", () => {
    it("should retrieve referral history with pagination", async () => {
      const testCode = "HISTORY_" + Date.now();
      await db.insert(referrals).values({
        referrerId: 1,
        refereeId: 0,
        referralCode: testCode,
        referralLink: `https://playplaycoinkrazy.com?ref=${testCode}`,
        source: "sms",
        status: "pending",
        rewardAmount: 1,
        createdAt: new Date(),
      } as any);

      const refs = await db
        .select()
        .from(referrals)
        .where(eq(referrals.referrerId, 1));

      const paginated = refs.slice(0, 20);
      expect(Array.isArray(paginated)).toBe(true);
      expect(paginated.length).toBeLessThanOrEqual(20);
    });

    it("should include referral metadata", async () => {
      const testCode = "METADATA_" + Date.now();
      await db.insert(referrals).values({
        referrerId: 1,
        refereeId: 0,
        referralCode: testCode,
        referralLink: `https://playplaycoinkrazy.com?ref=${testCode}`,
        source: "instagram",
        status: "pending",
        rewardAmount: 1,
        createdAt: new Date(),
      } as any);

      const refs = await db
        .select()
        .from(referrals)
        .where(eq(referrals.referralCode, testCode));

      if (refs.length > 0) {
        const ref = refs[0];
        expect(ref.referralCode).toBe(testCode);
        expect(ref.source).toBe("instagram");
        expect(ref.status).toBe("pending");
        expect(ref.rewardAmount).toBe(1);
      }
    });
  });

  describe("Referral Conversion", () => {
    it("should calculate conversion rate", async () => {
      const testCode1 = "CONV_1_" + Date.now();
      const testCode2 = "CONV_2_" + Date.now();

      await db.insert(referrals).values({
        referrerId: 1,
        refereeId: 0,
        referralCode: testCode1,
        referralLink: `https://playplaycoinkrazy.com?ref=${testCode1}`,
        source: "facebook",
        status: "completed",
        rewardAmount: 1,
        createdAt: new Date(),
        completedAt: new Date(),
      } as any);

      await db.insert(referrals).values({
        referrerId: 1,
        refereeId: 0,
        referralCode: testCode2,
        referralLink: `https://playplaycoinkrazy.com?ref=${testCode2}`,
        source: "twitter",
        status: "pending",
        rewardAmount: 1,
        createdAt: new Date(),
      } as any);

      const refs = await db
        .select()
        .from(referrals)
        .where(eq(referrals.referrerId, 1));

      const completed = refs.filter((r: any) => r.status === "completed").length;
      const total = refs.length;
      const conversionRate = total > 0 ? (completed / total) * 100 : 0;

      expect(conversionRate).toBeGreaterThanOrEqual(0);
      expect(conversionRate).toBeLessThanOrEqual(100);
    });
  });

  describe("Referral Sources", () => {
    it("should track referrals by source", async () => {
      const sources = ["facebook", "twitter", "email", "sms", "instagram", "messenger"];
      const sourceStats: Record<string, number> = {};

      for (const source of sources) {
        const testCode = `${source}_${Date.now()}`;
        await db.insert(referrals).values({
          referrerId: 1,
          refereeId: 0,
          referralCode: testCode,
          referralLink: `https://playplaycoinkrazy.com?ref=${testCode}`,
          source,
          status: "pending",
          rewardAmount: 1,
          createdAt: new Date(),
        } as any);
      }

      const refs = await db.select().from(referrals);
      refs.forEach((ref: any) => {
        sourceStats[ref.source] = (sourceStats[ref.source] || 0) + 1;
      });

      expect(Object.keys(sourceStats).length).toBeGreaterThan(0);
      Object.values(sourceStats).forEach((count: number) => {
        expect(count).toBeGreaterThan(0);
      });
    });
  });
});
