import { TestBed } from '@angular/core/testing';
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { RoleService } from './role.service';
import { TOKEN_PROVIDER, TokenProvider } from './token-provider';
import { Role } from './role';

describe('RoleService', () => {
  let service: RoleService;
  let tokenProvider: TokenProvider;

  beforeEach(() => {
    tokenProvider = {
      getClaimByKey: vi.fn(),
    };

    TestBed.configureTestingModule({
      providers: [RoleService, { provide: TOKEN_PROVIDER, useValue: tokenProvider }],
    });

    service = TestBed.inject(RoleService);
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  describe('hasRole', () => {
    it('should return true when user has single required role', () => {
      vi.spyOn(tokenProvider, 'getClaimByKey').mockReturnValue([Role.Store, Role.CallCenter]);

      expect(service.hasRole(Role.Store)).toBe(true);
    });

    it('should return false when user does not have required role', () => {
      vi.spyOn(tokenProvider, 'getClaimByKey').mockReturnValue([Role.CallCenter]);

      expect(service.hasRole(Role.Store)).toBe(false);
    });

    it('should return true when user has all required roles (array)', () => {
      vi.spyOn(tokenProvider, 'getClaimByKey').mockReturnValue([
        Role.Store,
        Role.CallCenter,
      ]);

      expect(service.hasRole([Role.Store, Role.CallCenter])).toBe(true);
    });

    it('should return false when user is missing one of required roles', () => {
      vi.spyOn(tokenProvider, 'getClaimByKey').mockReturnValue([Role.CallCenter]);

      expect(service.hasRole([Role.Store, Role.CallCenter])).toBe(false);
    });

    it('should return false when user has no roles in token', () => {
      vi.spyOn(tokenProvider, 'getClaimByKey').mockReturnValue(null);

      expect(service.hasRole(Role.Store)).toBe(false);
    });

    it('should return false when user has undefined roles', () => {
      vi.spyOn(tokenProvider, 'getClaimByKey').mockReturnValue(undefined);

      expect(service.hasRole(Role.Store)).toBe(false);
    });

    it('should handle errors gracefully', () => {
      vi.spyOn(tokenProvider, 'getClaimByKey').mockImplementation(() => {
        throw new Error('Token parsing error');
      });

      expect(service.hasRole(Role.Store)).toBe(false);
    });

    it('should handle empty role array', () => {
      vi.spyOn(tokenProvider, 'getClaimByKey').mockReturnValue([Role.Store]);

      expect(service.hasRole([])).toBe(true); // empty array means no requirements
    });

    it('should handle single role as string (not array)', () => {
      // JWT might return a single string instead of array for single role
      vi.spyOn(tokenProvider, 'getClaimByKey').mockReturnValue(Role.Store);

      expect(service.hasRole(Role.Store)).toBe(true);
      expect(service.hasRole(Role.CallCenter)).toBe(false);
    });

    it('should handle single role string when checking multiple roles', () => {
      vi.spyOn(tokenProvider, 'getClaimByKey').mockReturnValue(Role.Store);

      expect(service.hasRole([Role.Store, Role.CallCenter])).toBe(false);
    });
  });
});
