import { describe, test, expect, beforeEach, afterEach } from "@jest/globals";
import { prisma } from "../lib/db";
import { checkDashboardAccess } from "../lib/dashboard-access";
import { generateTestEmail } from "../tests/helpers/test-helpers";

/**
 * Dashboard Access Control Integration Tests
 * Tests the complete access control logic with real database
 */

// Requires live database - skip in unit test environment
describe.skip("Dashboard Access Control - Integration with Database", () => {
  let testUserId: string;
  let testEmail: string;

  beforeEach(async () => {
    testEmail = generateTestEmail();
  });

  // Helper to generate unique Stripe IDs for testing
  const generateStripeIds = () => {
    const timestamp = Date.now();
    const random = Math.random().toString(36).substring(7);
    return {
      stripeCustomerId: `cus_test_${timestamp}_${random}`,
      stripeSubscriptionId: `sub_test_${timestamp}_${random}`,
      stripePriceId: `price_test_${timestamp}_${random}`,
    };
  };

  afterEach(async () => {
    // Cleanup test user and subscriptions
    if (testUserId) {
      await prisma.subscriptions.deleteMany({
        where: { userId: testUserId },
      });
      await prisma.users
        .delete({
          where: { id: testUserId },
        })
        .catch(() => {
          // User might not exist, that's ok
        });
    }
  });

  test("User not found returns access denied with login redirect", async () => {
    const nonExistentUserId = "non-existent-user-id-12345";

    const result = await checkDashboardAccess(nonExistentUserId);

    expect(result.hasAccess).toBe(false);
    expect(result.reason).toBe("User not found");
    expect(result.redirectUrl).toBe("/en/login");
  });

  test("Incomplete onboarding denies access and redirects to signup", async () => {
    // Create user with incomplete onboarding
    const user = await prisma.users.create({
      data: {
        email: testEmail,
        password: "hashed_password",
        name: "Test User",
        onboardingComplete: false,
      },
    });
    testUserId = user.id;

    const result = await checkDashboardAccess(user.id);

    expect(result.hasAccess).toBe(false);
    expect(result.reason).toBe("Onboarding not complete");
    expect(result.onboardingComplete).toBe(false);
    expect(result.redirectUrl).toBe("/en/signup");
  });

  test("No subscription denies access even with complete onboarding", async () => {
    // Create user with complete onboarding but no subscription
    const user = await prisma.users.create({
      data: {
        email: testEmail,
        password: "hashed_password",
        name: "Test User",
        onboardingComplete: true,
      },
    });
    testUserId = user.id;

    const result = await checkDashboardAccess(user.id);

    expect(result.hasAccess).toBe(false);
    expect(result.reason).toBe("No active subscription");
    expect(result.onboardingComplete).toBe(true);
    expect(result.redirectUrl).toBe("/en/signup");
  });

  test("Active subscription grants access with subscription details", async () => {
    // Create user with complete onboarding and active subscription
    const user = await prisma.users.create({
      data: {
        email: testEmail,
        password: "hashed_password",
        name: "Test User",
        onboardingComplete: true,
      },
    });
    testUserId = user.id;

    const futureDate = new Date();
    futureDate.setMonth(futureDate.getMonth() + 1);

    const { stripeCustomerId, stripeSubscriptionId, stripePriceId } =
      generateStripeIds();
    const subscription = await prisma.subscriptions.create({
      data: {
        userId: user.id,
        status: "active",
        tierId: "tier2",
        billingPeriod: "monthly",
        currentPeriodEnd: futureDate,
        stripeCustomerId,
        stripeSubscriptionId,
        stripePriceId,
      },
    });

    const result = await checkDashboardAccess(user.id);

    expect(result.hasAccess).toBe(true);
    expect(result.onboardingComplete).toBe(true);
    expect(result.subscription).toBeDefined();
    expect(result.subscription?.status).toBe("active");
    expect(result.subscription?.tierId).toBe("tier2");
    expect(result.subscription?.billingPeriod).toBe("monthly");
  });

  test("Trialing subscription grants access", async () => {
    const user = await prisma.users.create({
      data: {
        email: testEmail,
        password: "hashed_password",
        name: "Test User",
        onboardingComplete: true,
      },
    });
    testUserId = user.id;

    const futureDate = new Date();
    futureDate.setDate(futureDate.getDate() + 7);

    const { stripeCustomerId, stripeSubscriptionId, stripePriceId } =
      generateStripeIds();
    await prisma.subscriptions.create({
      data: {
        userId: user.id,
        status: "trialing",
        tierId: "tier1",
        billingPeriod: "monthly",
        currentPeriodEnd: futureDate,
        stripeCustomerId,
        stripeSubscriptionId,
        stripePriceId,
      },
    });

    const result = await checkDashboardAccess(user.id);

    expect(result.hasAccess).toBe(true);
    expect(result.subscription?.status).toBe("trialing");
  });

  test("Pending verification subscription grants access", async () => {
    const user = await prisma.users.create({
      data: {
        email: testEmail,
        password: "hashed_password",
        name: "Test User",
        onboardingComplete: true,
      },
    });
    testUserId = user.id;

    const futureDate = new Date();
    futureDate.setMonth(futureDate.getMonth() + 1);

    const { stripeCustomerId, stripeSubscriptionId, stripePriceId } =
      generateStripeIds();
    await prisma.subscriptions.create({
      data: {
        userId: user.id,
        status: "pending_verification",
        tierId: "tier2",
        billingPeriod: "monthly",
        currentPeriodEnd: futureDate,
        stripeCustomerId,
        stripeSubscriptionId,
        stripePriceId,
      },
    });

    const result = await checkDashboardAccess(user.id);

    expect(result.hasAccess).toBe(true);
    expect(result.subscription?.status).toBe("pending_verification");
  });

  test("Past due subscription denies access and redirects to pricing", async () => {
    const user = await prisma.users.create({
      data: {
        email: testEmail,
        password: "hashed_password",
        name: "Test User",
        onboardingComplete: true,
      },
    });
    testUserId = user.id;

    const pastDate = new Date();
    pastDate.setDate(pastDate.getDate() - 1);

    const { stripeCustomerId, stripeSubscriptionId, stripePriceId } =
      generateStripeIds();
    await prisma.subscriptions.create({
      data: {
        userId: user.id,
        status: "past_due",
        tierId: "tier2",
        billingPeriod: "monthly",
        currentPeriodEnd: pastDate,
        stripeCustomerId,
        stripeSubscriptionId,
        stripePriceId,
      },
    });

    const result = await checkDashboardAccess(user.id);

    expect(result.hasAccess).toBe(false);
    expect(result.reason).toContain("past_due");
    expect(result.onboardingComplete).toBe(true);
    expect(result.subscription?.status).toBe("past_due");
    expect(result.redirectUrl).toBe("/en/pricing");
  });

  test("Suspended subscription denies access", async () => {
    const user = await prisma.users.create({
      data: {
        email: testEmail,
        password: "hashed_password",
        name: "Test User",
        onboardingComplete: true,
      },
    });
    testUserId = user.id;

    const futureDate = new Date();
    futureDate.setMonth(futureDate.getMonth() + 1);

    const { stripeCustomerId, stripeSubscriptionId, stripePriceId } =
      generateStripeIds();
    await prisma.subscriptions.create({
      data: {
        userId: user.id,
        status: "suspended",
        tierId: "tier2",
        billingPeriod: "monthly",
        currentPeriodEnd: futureDate,
        stripeCustomerId,
        stripeSubscriptionId,
        stripePriceId,
      },
    });

    const result = await checkDashboardAccess(user.id);

    expect(result.hasAccess).toBe(false);
    expect(result.reason).toContain("suspended");
    expect(result.redirectUrl).toBe("/en/signup");
  });

  test("Cancelled subscription denies access", async () => {
    const user = await prisma.users.create({
      data: {
        email: testEmail,
        password: "hashed_password",
        name: "Test User",
        onboardingComplete: true,
      },
    });
    testUserId = user.id;

    const futureDate = new Date();
    futureDate.setMonth(futureDate.getMonth() + 1);

    const { stripeCustomerId, stripeSubscriptionId, stripePriceId } =
      generateStripeIds();
    await prisma.subscriptions.create({
      data: {
        userId: user.id,
        status: "cancelled",
        tierId: "tier2",
        billingPeriod: "monthly",
        currentPeriodEnd: futureDate,
        stripeCustomerId,
        stripeSubscriptionId,
        stripePriceId,
      },
    });

    const result = await checkDashboardAccess(user.id);

    expect(result.hasAccess).toBe(false);
    expect(result.reason).toContain("cancelled");
  });

  test("Multiple subscriptions uses most recent one", async () => {
    const user = await prisma.users.create({
      data: {
        email: testEmail,
        password: "hashed_password",
        name: "Test User",
        onboardingComplete: true,
      },
    });
    testUserId = user.id;

    const futureDate = new Date();
    futureDate.setMonth(futureDate.getMonth() + 1);

    // Create old subscription
    const oldIds = generateStripeIds();
    await prisma.subscriptions.create({
      data: {
        userId: user.id,
        status: "cancelled",
        tierId: "tier1",
        billingPeriod: "monthly",
        currentPeriodEnd: futureDate,
        createdAt: new Date("2024-01-01"),
        stripeCustomerId: oldIds.stripeCustomerId,
        stripeSubscriptionId: oldIds.stripeSubscriptionId,
        stripePriceId: oldIds.stripePriceId,
      },
    });

    // Create newer subscription
    const newIds = generateStripeIds();
    await prisma.subscriptions.create({
      data: {
        userId: user.id,
        status: "active",
        tierId: "tier3",
        billingPeriod: "yearly",
        currentPeriodEnd: futureDate,
        createdAt: new Date("2024-12-01"),
        stripeCustomerId: newIds.stripeCustomerId,
        stripeSubscriptionId: newIds.stripeSubscriptionId,
        stripePriceId: newIds.stripePriceId,
      },
    });

    const result = await checkDashboardAccess(user.id);

    expect(result.hasAccess).toBe(true);
    expect(result.subscription?.status).toBe("active");
    expect(result.subscription?.tierId).toBe("tier3");
    expect(result.subscription?.billingPeriod).toBe("yearly");
  });

  test("Subscription data includes properly formatted ISO date", async () => {
    const user = await prisma.users.create({
      data: {
        email: testEmail,
        password: "hashed_password",
        name: "Test User",
        onboardingComplete: true,
      },
    });
    testUserId = user.id;

    const expiryDate = new Date("2025-03-15T12:30:45.000Z");

    const { stripeCustomerId, stripeSubscriptionId, stripePriceId } =
      generateStripeIds();
    await prisma.subscriptions.create({
      data: {
        userId: user.id,
        status: "active",
        tierId: "tier2",
        billingPeriod: "monthly",
        currentPeriodEnd: expiryDate,
        stripeCustomerId,
        stripeSubscriptionId,
        stripePriceId,
      },
    });

    const result = await checkDashboardAccess(user.id);

    expect(result.hasAccess).toBe(true);
    expect(result.subscription?.currentPeriodEnd).toBe(
      expiryDate.toISOString(),
    );
  });

  test("Access check handles database errors gracefully", async () => {
    // This test verifies error handling
    // In production, if database query fails, should return safe error response

    // Create a user to test with
    const user = await prisma.users.create({
      data: {
        email: testEmail,
        password: "hashed_password",
        name: "Test User",
        onboardingComplete: true,
      },
    });
    testUserId = user.id;

    const { stripeCustomerId, stripeSubscriptionId, stripePriceId } =
      generateStripeIds();
    await prisma.subscriptions.create({
      data: {
        userId: user.id,
        status: "active",
        tierId: "tier2",
        billingPeriod: "monthly",
        currentPeriodEnd: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
        stripeCustomerId,
        stripeSubscriptionId,
        stripePriceId,
      },
    });

    // Normal case works - user with valid active subscription should have access
    const result = await checkDashboardAccess(user.id);
    expect(result.hasAccess).toBe(true);
    expect(result.subscription?.status).toBe("active");

    // Function should not throw, even on edge cases
    const resultForInvalidId = await checkDashboardAccess("invalid-id-format");
    expect(resultForInvalidId).toBeDefined();
    expect(resultForInvalidId.hasAccess).toBe(false);
  });
});

describe.skip("Dashboard Access Control - Helper Functions", () => {
  let testUserId: string;
  let testEmail: string;

  beforeEach(async () => {
    testEmail = generateTestEmail();
  });

  // Helper to generate unique Stripe IDs for testing
  const generateStripeIds = () => {
    const timestamp = Date.now();
    const random = Math.random().toString(36).substring(7);
    return {
      stripeCustomerId: `cus_test_${timestamp}_${random}`,
      stripeSubscriptionId: `sub_test_${timestamp}_${random}`,
      stripePriceId: `price_test_${timestamp}_${random}`,
    };
  };

  afterEach(async () => {
    if (testUserId) {
      await prisma.subscriptions.deleteMany({
        where: { userId: testUserId },
      });
      await prisma.users
        .delete({
          where: { id: testUserId },
        })
        .catch(() => {
          // User might not exist, that's ok
        });
    }
  });

  test("getUserDashboardContext returns full user data", async () => {
    const { getUserDashboardContext } = await import("../lib/dashboard-access");

    const user = await prisma.users.create({
      data: {
        email: testEmail,
        password: "hashed_password",
        name: "Test User",
        onboardingComplete: true,
        companyName: "Test Company",
        country: "UAE",
        selectedPackage: "tier2",
        goals: ["reduce_no_shows", "automate_reminders"],
      },
    });
    testUserId = user.id;

    const futureDate = new Date();
    futureDate.setMonth(futureDate.getMonth() + 1);

    const { stripeCustomerId, stripeSubscriptionId, stripePriceId } =
      generateStripeIds();
    await prisma.subscriptions.create({
      data: {
        userId: user.id,
        status: "active",
        tierId: "tier2",
        billingPeriod: "monthly",
        currentPeriodEnd: futureDate,
        trialEndsAt: new Date("2025-01-20"),
        stripeCustomerId,
        stripeSubscriptionId,
        stripePriceId,
      },
    });

    const context = await getUserDashboardContext(user.id);

    expect(context).toBeDefined();
    expect(context?.email).toBe(testEmail);
    expect(context?.name).toBe("Test User");
    expect(context?.onboardingComplete).toBe(true);
    expect(context?.companyName).toBe("Test Company");
    expect(context?.subscriptions).toHaveLength(1);
    expect(context?.subscriptions[0].status).toBe("active");
  });

  test("completeOnboarding marks user as onboarded", async () => {
    const { completeOnboarding } = await import("../lib/dashboard-access");

    const user = await prisma.users.create({
      data: {
        email: testEmail,
        password: "hashed_password",
        name: "Test User",
        onboardingComplete: false,
      },
    });
    testUserId = user.id;

    // Initially incomplete
    expect(user.onboardingComplete).toBe(false);

    // Mark as complete
    const result = await completeOnboarding(user.id);
    expect(result).toBe(true);

    // Verify updated
    const updatedUser = await prisma.users.findUnique({
      where: { id: user.id },
    });
    expect(updatedUser?.onboardingComplete).toBe(true);
  });

  test("completeOnboarding handles invalid user gracefully", async () => {
    const { completeOnboarding } = await import("../lib/dashboard-access");

    const result = await completeOnboarding("non-existent-user");

    expect(result).toBe(false);
  });
});
