import { describe, it, expect, beforeEach, vi } from "vitest";
import { Test, TestingModule } from "@nestjs/testing";
import { ExecutionContext, UnauthorizedException } from "@nestjs/common";
import { AuthGuard } from "./auth.guard";
import { AuthService } from "../auth.service";

describe("AuthGuard", () => {
  let guard: AuthGuard;
  let authService: AuthService;

  const mockAuthService = {
    verifySession: vi.fn(),
  };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        AuthGuard,
        {
          provide: AuthService,
          useValue: mockAuthService,
        },
      ],
    }).compile();

    guard = module.get<AuthGuard>(AuthGuard);
    authService = module.get<AuthService>(AuthService);

    vi.clearAllMocks();
  });

  const createMockExecutionContext = (headers: any = {}): ExecutionContext => {
    const mockRequest = {
      headers,
    };

    return {
      switchToHttp: () => ({
        getRequest: () => mockRequest,
      }),
    } as ExecutionContext;
  };

  describe("canActivate", () => {
    it("should return true for valid session", async () => {
      const mockSessionData = {
        user: {
          id: "user-123",
          email: "test@example.com",
          name: "Test User",
        },
        session: {
          id: "session-123",
        },
      };

      mockAuthService.verifySession.mockResolvedValue(mockSessionData);

      const context = createMockExecutionContext({
        authorization: "Bearer valid-token",
      });

      const result = await guard.canActivate(context);

      expect(result).toBe(true);
      expect(mockAuthService.verifySession).toHaveBeenCalledWith("valid-token");
    });

    it("should throw UnauthorizedException if no token provided", async () => {
      const context = createMockExecutionContext({});

      await expect(guard.canActivate(context)).rejects.toThrow(UnauthorizedException);
      await expect(guard.canActivate(context)).rejects.toThrow("No authentication token provided");
    });

    it("should throw UnauthorizedException if session is invalid", async () => {
      mockAuthService.verifySession.mockResolvedValue(null);

      const context = createMockExecutionContext({
        authorization: "Bearer invalid-token",
      });

      await expect(guard.canActivate(context)).rejects.toThrow(UnauthorizedException);
      await expect(guard.canActivate(context)).rejects.toThrow("Invalid or expired session");
    });

    it("should throw UnauthorizedException if session verification fails", async () => {
      mockAuthService.verifySession.mockRejectedValue(new Error("Verification failed"));

      const context = createMockExecutionContext({
        authorization: "Bearer error-token",
      });

      await expect(guard.canActivate(context)).rejects.toThrow(UnauthorizedException);
      await expect(guard.canActivate(context)).rejects.toThrow("Authentication failed");
    });
  });
});
