/**
 * Unit Tests: Authentication Helper Functions
 *
 * Tests dual authentication system helpers:
 * - getAuthenticatedEmail()
 * - getAuthenticatedUser()
 *
 * Ensures both NextAuth and auth_token methods work correctly
 */

import { NextRequest } from "next/server";
import {
  getAuthenticatedEmail,
  getAuthenticatedUser,
} from "@/lib/utils/auth-helpers.util";
import {
  createMockNextAuthSession,
  createMockAuthFunction,
  createAuthTokenCookie,
  createMockRequestWithAuthToken,
  createMockRequestNoAuth,
  TEST_USER,
  OTP_USER,
} from "../helpers/auth-test-helpers";
import { prisma } from "@/lib/db";

// Mock Prisma
jest.mock("@/lib/db");

describe("getAuthenticatedEmail", () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  describe("NextAuth session authentication", () => {
    it("should return email from valid NextAuth session", async () => {
      const session = createMockNextAuthSession(TEST_USER.id, TEST_USER.email);
      const authFn = createMockAuthFunction(session);
      const request = createMockRequestNoAuth();

      const email = await getAuthenticatedEmail(request, authFn);

      expect(email).toBe(TEST_USER.email);
    });

    it("should return null for invalid NextAuth session", async () => {
      const authFn = createMockAuthFunction(null);
      const request = createMockRequestNoAuth();

      const email = await getAuthenticatedEmail(request, authFn);

      expect(email).toBeNull();
    });
  });

  describe("auth_token cookie authentication", () => {
    it.skip("should return email from valid auth_token cookie", async () => {
      const authFn = createMockAuthFunction(null); // No NextAuth session
      const request = createMockRequestWithAuthToken(
        OTP_USER.id,
        OTP_USER.email,
      );

      const email = await getAuthenticatedEmail(request, authFn);

      expect(email).toBe(OTP_USER.email);
    });

    it("should handle malformed auth_token cookie", async () => {
      const authFn = createMockAuthFunction(null);
      const request = {
        url: "http://localhost:9000/api/test",
        method: "GET",
        cookies: {
          get: (name: string) => {
            if (name === "auth_token") {
              return { name: "auth_token", value: "invalid-base64-!@#$" };
            }
            return undefined;
          },
        },
        headers: {
          get: () => null,
        },
      };

      const email = await getAuthenticatedEmail(request as any, authFn);

      expect(email).toBeNull();
    });

    it("should handle auth_token with missing email", async () => {
      const authFn = createMockAuthFunction(null);
      const tokenData = { userId: "user-123", timestamp: Date.now() }; // No email
      const authToken = Buffer.from(JSON.stringify(tokenData)).toString(
        "base64",
      );
      const request = {
        url: "http://localhost:9000/api/test",
        method: "GET",
        cookies: {
          get: (name: string) => {
            if (name === "auth_token") {
              return { name: "auth_token", value: authToken };
            }
            return undefined;
          },
        },
        headers: {
          get: () => null,
        },
      };

      const email = await getAuthenticatedEmail(request as any, authFn);

      expect(email).toBeNull();
    });
  });

  describe("priority handling", () => {
    it.skip("should prefer NextAuth session over auth_token cookie", async () => {
      // Both auth methods present
      const session = createMockNextAuthSession(TEST_USER.id, TEST_USER.email);
      const authFn = createMockAuthFunction(session);
      const request = createMockRequestWithAuthToken(
        OTP_USER.id,
        OTP_USER.email,
      );

      const email = await getAuthenticatedEmail(request, authFn);

      // Should return NextAuth email, not auth_token email
      expect(email).toBe(TEST_USER.email);
    });
  });

  describe("no authentication", () => {
    it("should return null when no auth method present", async () => {
      const authFn = createMockAuthFunction(null);
      const request = createMockRequestNoAuth();

      const email = await getAuthenticatedEmail(request, authFn);

      expect(email).toBeNull();
    });
  });
});

describe("getAuthenticatedUser", () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  describe("NextAuth session authentication", () => {
    it.skip("should return user data from valid NextAuth session", async () => {
      const session = createMockNextAuthSession(TEST_USER.id, TEST_USER.email, {
        name: TEST_USER.name,
        stripeCustomerId: TEST_USER.stripeCustomerId,
      });
      const authFn = createMockAuthFunction(session);
      const request = createMockRequestNoAuth();

      const user = await getAuthenticatedUser(request, authFn);

      expect(user).toEqual({
        userId: TEST_USER.id,
        email: TEST_USER.email,
        name: TEST_USER.name,
        stripeCustomerId: TEST_USER.stripeCustomerId,
        source: "nextauth",
      });
    });

    it.skip("should handle NextAuth session without optional fields", async () => {
      const session = createMockNextAuthSession(TEST_USER.id, TEST_USER.email);
      const authFn = createMockAuthFunction(session);
      const request = createMockRequestNoAuth();

      const user = await getAuthenticatedUser(request, authFn);

      expect(user).toEqual({
        userId: TEST_USER.id,
        email: TEST_USER.email,
        name: undefined,
        stripeCustomerId: undefined,
        source: "nextauth",
      });
    });
  });

  describe("auth_token cookie authentication", () => {
    it.skip("should return user data from valid auth_token cookie", async () => {
      // Mock database user lookup
      (prisma.users.findUnique as jest.Mock).mockResolvedValue({
        id: OTP_USER.id,
        email: OTP_USER.email,
        name: OTP_USER.name,
        stripeCustomerId: OTP_USER.stripeCustomerId,
      });

      const authFn = createMockAuthFunction(null);
      const request = createMockRequestWithAuthToken(
        OTP_USER.id,
        OTP_USER.email,
        {
          fullName: OTP_USER.name,
        },
      );

      const user = await getAuthenticatedUser(request, authFn);

      expect(user).toEqual({
        userId: OTP_USER.id,
        email: OTP_USER.email,
        name: OTP_USER.name,
        stripeCustomerId: OTP_USER.stripeCustomerId,
        source: "auth_token",
      });

      // Verify database was queried
      expect(prisma.users.findUnique).toHaveBeenCalledWith({
        where: { id: OTP_USER.id },
        select: {
          id: true,
          email: true,
          name: true,
          stripeCustomerId: true,
        },
      });
    });

    it("should return null if user not found in database", async () => {
      (prisma.users.findUnique as jest.Mock).mockResolvedValue(null);

      const authFn = createMockAuthFunction(null);
      const request = createMockRequestWithAuthToken(
        "nonexistent-user",
        "fake@example.com",
      );

      const user = await getAuthenticatedUser(request, authFn);

      expect(user).toBeNull();
    });

    it("should handle auth_token with missing userId", async () => {
      const authFn = createMockAuthFunction(null);
      const tokenData = { email: "test@example.com", timestamp: Date.now() }; // No userId
      const authToken = Buffer.from(JSON.stringify(tokenData)).toString(
        "base64",
      );
      const request = {
        url: "http://localhost:9000/api/test",
        method: "GET",
        cookies: {
          get: (name: string) => {
            if (name === "auth_token") {
              return { name: "auth_token", value: authToken };
            }
            return undefined;
          },
        },
        headers: {
          get: () => null,
        },
      };

      const user = await getAuthenticatedUser(request as any, authFn);

      expect(user).toBeNull();
    });
  });

  describe("priority handling", () => {
    it.skip("should prefer NextAuth session over auth_token cookie", async () => {
      const session = createMockNextAuthSession(TEST_USER.id, TEST_USER.email, {
        stripeCustomerId: TEST_USER.stripeCustomerId,
      });
      const authFn = createMockAuthFunction(session);
      const request = createMockRequestWithAuthToken(
        OTP_USER.id,
        OTP_USER.email,
      );

      const user = await getAuthenticatedUser(request, authFn);

      // Should return NextAuth user, not auth_token user
      expect(user?.userId).toBe(TEST_USER.id);
      expect(user?.email).toBe(TEST_USER.email);
      expect(user?.source).toBe("nextauth");

      // Database should NOT be queried when NextAuth session exists
      expect(prisma.users.findUnique).not.toHaveBeenCalled();
    });
  });

  describe("no authentication", () => {
    it("should return null when no auth method present", async () => {
      const authFn = createMockAuthFunction(null);
      const request = createMockRequestNoAuth();

      const user = await getAuthenticatedUser(request, authFn);

      expect(user).toBeNull();
    });
  });
});

/**
 * Regression test: Ensure helper functions are properly exported
 */
describe("auth-helpers module exports", () => {
  it("should export getAuthenticatedEmail function", () => {
    expect(typeof getAuthenticatedEmail).toBe("function");
  });

  it("should export getAuthenticatedUser function", () => {
    expect(typeof getAuthenticatedUser).toBe("function");
  });
});
