import { describe, it, expect } from "vitest";
import {
  generateAccessToken,
  generateRefreshToken,
  verifyAccessToken,
  verifyRefreshToken,
  generateTokens,
  isTokenExpired,
} from "../jwt-utils";
import type { SelectUser } from "$lib/server/db/central-schema";

const mockUser: SelectUser = {
  id: "test-user-id",
  email: "test@example.com",
  name: "Test User",
  role: "GLOBAL_ADMIN",
  tenantId: null,
  createdAt: new Date(),
  updatedAt: new Date(),
  lastLoginAt: new Date(),
  isActive: true,
  confirmationState: "ACCESS_GRANTED" as const,
  token: null,
  tokenValidUntil: null,
  passphraseHash: null,
  recoveryPassphrase: null,
  language: "de",
};

describe("JWT Utils", () => {
  const sessionId = "test-session-id";

  describe("generateAccessToken", () => {
    it("should generate a valid access token", async () => {
      const token = await generateAccessToken(mockUser, sessionId);
      expect(token).toBeDefined();
      expect(typeof token).toBe("string");
      expect(token.split(".")).toHaveLength(3);
    });

    it("should include user data in token payload", async () => {
      const token = await generateAccessToken(mockUser, sessionId);
      const payload = await verifyAccessToken(token);

      expect(payload).toBeDefined();
      expect(payload!.userId).toBe(mockUser.id);
      expect(payload!.email).toBe(mockUser.email);
      expect(payload!.name).toBe(mockUser.name);
      expect(payload!.role).toBe(mockUser.role);
      expect(payload!.sessionId).toBe(sessionId);
    });
  });

  describe("generateRefreshToken", () => {
    it("should generate a valid refresh token", async () => {
      const token = await generateRefreshToken(mockUser.id, sessionId);
      expect(token).toBeDefined();
      expect(typeof token).toBe("string");
      expect(token.split(".")).toHaveLength(3);
    });

    it("should include user and session data in token payload", async () => {
      const token = await generateRefreshToken(mockUser.id, sessionId);
      const payload = await verifyRefreshToken(token);

      expect(payload).toBeDefined();
      expect(payload!.userId).toBe(mockUser.id);
      expect(payload!.sessionId).toBe(sessionId);
    });
  });

  describe("verifyAccessToken", () => {
    it("should verify a valid access token", async () => {
      const token = await generateAccessToken(mockUser, sessionId);
      const payload = await verifyAccessToken(token);

      expect(payload).toBeDefined();
      expect(payload!.userId).toBe(mockUser.id);
    });

    it("should reject invalid tokens", async () => {
      const payload = await verifyAccessToken("invalid.token.here");
      expect(payload).toBeNull();
    });

    it("should reject malformed tokens", async () => {
      const payload = await verifyAccessToken("invalid-token");
      expect(payload).toBeNull();
    });
  });

  describe("verifyRefreshToken", () => {
    it("should verify a valid refresh token", async () => {
      const token = await generateRefreshToken(mockUser.id, sessionId);
      const payload = await verifyRefreshToken(token);

      expect(payload).toBeDefined();
      expect(payload!.userId).toBe(mockUser.id);
      expect(payload!.sessionId).toBe(sessionId);
    });

    it("should reject invalid tokens", async () => {
      const payload = await verifyRefreshToken("invalid.token.here");
      expect(payload).toBeNull();
    });

    it("should reject access tokens as refresh tokens", async () => {
      const accessToken = await generateAccessToken(mockUser, sessionId);
      const payload = await verifyRefreshToken(accessToken);
      expect(payload).toBeNull();
    });
  });

  describe("generateTokens", () => {
    it("should generate both access and refresh tokens", async () => {
      const tokens = await generateTokens(mockUser, sessionId);

      expect(tokens.accessToken).toBeDefined();
      expect(tokens.refreshToken).toBeDefined();
      expect(typeof tokens.accessToken).toBe("string");
      expect(typeof tokens.refreshToken).toBe("string");
    });

    it("should generate verifiable tokens", async () => {
      const tokens = await generateTokens(mockUser, sessionId);

      const accessPayload = await verifyAccessToken(tokens.accessToken);
      const refreshPayload = await verifyRefreshToken(tokens.refreshToken);

      expect(accessPayload).toBeDefined();
      expect(refreshPayload).toBeDefined();
      expect(accessPayload!.userId).toBe(mockUser.id);
      expect(refreshPayload!.userId).toBe(mockUser.id);
    });
  });

  describe("isTokenExpired", () => {
    it("should return false for valid tokens", async () => {
      const token = await generateAccessToken(mockUser, sessionId);
      expect(await isTokenExpired(token)).toBe(false);
    });

    it("should return true for malformed tokens", async () => {
      expect(await isTokenExpired("invalid-token")).toBe(true);
    });

    it("should return true for empty tokens", async () => {
      expect(await isTokenExpired("")).toBe(true);
    });
  });

  describe("Token expiration", () => {
    it("should respect token expiration", async () => {
      // Test with an obviously expired token (JWT with past exp claim)
      // This is a manually crafted expired JWT for testing purposes
      const expiredToken =
        "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiJ0ZXN0LXVzZXItaWQiLCJlbWFpbCI6InRlc3RAZXhhbXBsZS5jb20iLCJuYW1lIjoiVGVzdCBVc2VyIiwicm9sZSI6IkdMT0JBTF9BRE1JTiIsInNlc3Npb25JZCI6InRlc3Qtc2Vzc2lvbi1pZCIsImlhdCI6MTYwMDAwMDAwMCwiZXhwIjoxNjAwMDAwOTAwfQ.";

      const payload = await verifyAccessToken(expiredToken);
      expect(payload).toBeNull();
    });
  });
});
