import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
import { ForbiddenException } from '@nestjs/common';
import { requireLeagueAdminOrOwner } from './LeagueAuthorization';

// Mock the auth module
vi.mock('../auth/getActorFromRequestContext', () => ({
  getActorFromRequestContext: vi.fn(),
}));

import { getActorFromRequestContext } from '../auth/getActorFromRequestContext';

describe('requireLeagueAdminOrOwner', () => {
  const mockGetActorFromRequestContext = vi.mocked(getActorFromRequestContext);
  const mockGetLeagueAdminPermissionsUseCase = {
    execute: vi.fn(),
  };

  beforeEach(() => {
    vi.clearAllMocks();
  });

  afterEach(() => {
    vi.restoreAllMocks();
  });

  it('should allow access for demo session role "league-admin"', async () => {
    mockGetActorFromRequestContext.mockReturnValue({
      userId: 'user-123',
      driverId: 'driver-123',
      role: 'league-admin',
    });

    await expect(
      requireLeagueAdminOrOwner('league-123', mockGetLeagueAdminPermissionsUseCase)
    ).resolves.not.toThrow();

    expect(mockGetLeagueAdminPermissionsUseCase.execute).not.toHaveBeenCalled();
  });

  it('should allow access for demo session role "league-owner"', async () => {
    mockGetActorFromRequestContext.mockReturnValue({
      userId: 'user-123',
      driverId: 'driver-123',
      role: 'league-owner',
    });

    await expect(
      requireLeagueAdminOrOwner('league-123', mockGetLeagueAdminPermissionsUseCase)
    ).resolves.not.toThrow();

    expect(mockGetLeagueAdminPermissionsUseCase.execute).not.toHaveBeenCalled();
  });

  it('should allow access for demo session role "super-admin"', async () => {
    mockGetActorFromRequestContext.mockReturnValue({
      userId: 'user-123',
      driverId: 'driver-123',
      role: 'super-admin',
    });

    await expect(
      requireLeagueAdminOrOwner('league-123', mockGetLeagueAdminPermissionsUseCase)
    ).resolves.not.toThrow();

    expect(mockGetLeagueAdminPermissionsUseCase.execute).not.toHaveBeenCalled();
  });

  it('should allow access for demo session role "system-owner"', async () => {
    mockGetActorFromRequestContext.mockReturnValue({
      userId: 'user-123',
      driverId: 'driver-123',
      role: 'system-owner',
    });

    await expect(
      requireLeagueAdminOrOwner('league-123', mockGetLeagueAdminPermissionsUseCase)
    ).resolves.not.toThrow();

    expect(mockGetLeagueAdminPermissionsUseCase.execute).not.toHaveBeenCalled();
  });

  it('should check permissions for non-demo roles', async () => {
    mockGetActorFromRequestContext.mockReturnValue({
      userId: 'user-123',
      driverId: 'driver-123',
      role: 'user',
    });

    const mockResult = {
      isErr: () => false,
    };

    mockGetLeagueAdminPermissionsUseCase.execute.mockResolvedValue(mockResult);

    await expect(
      requireLeagueAdminOrOwner('league-123', mockGetLeagueAdminPermissionsUseCase)
    ).resolves.not.toThrow();

    expect(mockGetLeagueAdminPermissionsUseCase.execute).toHaveBeenCalledWith({
      leagueId: 'league-123',
      performerDriverId: 'driver-123',
    });
  });

  it('should throw ForbiddenException when permission check fails', async () => {
    mockGetActorFromRequestContext.mockReturnValue({
      userId: 'user-123',
      driverId: 'driver-123',
      role: 'user',
    });

    const mockResult = {
      isErr: () => true,
    };

    mockGetLeagueAdminPermissionsUseCase.execute.mockResolvedValue(mockResult);

    await expect(
      requireLeagueAdminOrOwner('league-123', mockGetLeagueAdminPermissionsUseCase)
    ).rejects.toThrow(ForbiddenException);

    expect(mockGetLeagueAdminPermissionsUseCase.execute).toHaveBeenCalledWith({
      leagueId: 'league-123',
      performerDriverId: 'driver-123',
    });
  });

  it('should throw ForbiddenException with correct message', async () => {
    mockGetActorFromRequestContext.mockReturnValue({
      userId: 'user-123',
      driverId: 'driver-123',
      role: 'user',
    });

    const mockResult = {
      isErr: () => true,
    };

    mockGetLeagueAdminPermissionsUseCase.execute.mockResolvedValue(mockResult);

    try {
      await requireLeagueAdminOrOwner('league-123', mockGetLeagueAdminPermissionsUseCase);
      expect(true).toBe(false); // Should not reach here
    } catch (error) {
      expect(error).toBeInstanceOf(ForbiddenException);
      expect(error.message).toBe('Forbidden');
    }
  });

  it('should handle different league IDs', async () => {
    mockGetActorFromRequestContext.mockReturnValue({
      userId: 'user-123',
      driverId: 'driver-123',
      role: 'user',
    });

    const mockResult = {
      isErr: () => false,
    };

    mockGetLeagueAdminPermissionsUseCase.execute.mockResolvedValue(mockResult);

    await requireLeagueAdminOrOwner('league-456', mockGetLeagueAdminPermissionsUseCase);

    expect(mockGetLeagueAdminPermissionsUseCase.execute).toHaveBeenCalledWith({
      leagueId: 'league-456',
      performerDriverId: 'driver-123',
    });
  });

  it('should handle actor without role', async () => {
    mockGetActorFromRequestContext.mockReturnValue({
      userId: 'user-123',
      driverId: 'driver-123',
      role: undefined,
    });

    const mockResult = {
      isErr: () => false,
    };

    mockGetLeagueAdminPermissionsUseCase.execute.mockResolvedValue(mockResult);

    await expect(
      requireLeagueAdminOrOwner('league-123', mockGetLeagueAdminPermissionsUseCase)
    ).resolves.not.toThrow();

    expect(mockGetLeagueAdminPermissionsUseCase.execute).toHaveBeenCalled();
  });

  it('should handle actor with null role', async () => {
    mockGetActorFromRequestContext.mockReturnValue({
      userId: 'user-123',
      driverId: 'driver-123',
      role: null,
    });

    const mockResult = {
      isErr: () => false,
    };

    mockGetLeagueAdminPermissionsUseCase.execute.mockResolvedValue(mockResult);

    await expect(
      requireLeagueAdminOrOwner('league-123', mockGetLeagueAdminPermissionsUseCase)
    ).resolves.not.toThrow();

    expect(mockGetLeagueAdminPermissionsUseCase.execute).toHaveBeenCalled();
  });
});
