import { TestBed } from '@angular/core/testing';
import { of, throwError } from 'rxjs';
import { AdminUsersFacade } from './admin-users.facade';
import { ADMIN_USERS_GATEWAY } from '../services/admin-users-gateway.token';
import { AdminUsersGateway } from '../services/admin-users-gateway.interface';
import { AdminUser, AdminUserRoleRecord } from '../models/admin-user.model';

function user(overrides: Partial<AdminUser> = {}): AdminUser {
  return { id: 'u1', name: 'Alice', email: 'a@example.com', roleId: 'role1', scope: 'marketplace', status: 'active', ...overrides } as AdminUser;
}

describe('AdminUsersFacade', () => {
  let facade: AdminUsersFacade;
  let gateway: jasmine.SpyObj<AdminUsersGateway>;

  beforeEach(() => {
    gateway = jasmine.createSpyObj<AdminUsersGateway>('AdminUsersGateway', [
      'loadUsers', 'loadRoles', 'loadInvitations', 'loadSessions', 'loadAudit',
      'setUserRole', 'setUserStatus', 'inviteUser', 'revokeInvitation', 'revokeSession',
    ]);
    TestBed.configureTestingModule({ providers: [{ provide: ADMIN_USERS_GATEWAY, useValue: gateway }] });
    facade = TestBed.inject(AdminUsersFacade);
  });

  it('loadAll populates users, roles, and invitations independently', () => {
    gateway.loadUsers.and.returnValue(of([user()]));
    gateway.loadRoles.and.returnValue(of([{ id: 'role1', name: 'Manager' } as AdminUserRoleRecord]));
    gateway.loadInvitations.and.returnValue(of([]));

    facade.loadAll();

    expect(facade.users().length).toBe(1);
    expect(facade.roles().length).toBe(1);
  });

  it('a users-load failure clears users and sets error, without blocking roles/invitations', () => {
    gateway.loadUsers.and.returnValue(throwError(() => new Error('x')));
    gateway.loadRoles.and.returnValue(of([{ id: 'role1', name: 'Manager' } as AdminUserRoleRecord]));
    gateway.loadInvitations.and.returnValue(of([]));

    facade.loadAll();

    expect(facade.users()).toEqual([]);
    expect(facade.error()).toBeTrue();
    expect(facade.roles().length).toBe(1);
  });

  describe('roleName', () => {
    it('resolves a known role id to its display name', () => {
      gateway.loadUsers.and.returnValue(of([]));
      gateway.loadRoles.and.returnValue(of([{ id: 'role1', name: 'Manager' } as AdminUserRoleRecord]));
      gateway.loadInvitations.and.returnValue(of([]));
      facade.loadAll();

      expect(facade.roleName('role1')).toBe('Manager');
    });

    it('falls back to the raw id when the role is unknown, rather than showing blank', () => {
      expect(facade.roleName('mystery-role')).toBe('mystery-role');
    });
  });

  describe('invite', () => {
    it('does not call the gateway for a whitespace-only email', () => {
      facade.invite('   ', 'role1', 'marketplace');

      expect(gateway.inviteUser).not.toHaveBeenCalled();
    });

    it('trims the email before sending it', () => {
      gateway.inviteUser.and.returnValue(of({} as any));
      gateway.loadUsers.and.returnValue(of([]));
      gateway.loadRoles.and.returnValue(of([]));
      gateway.loadInvitations.and.returnValue(of([]));

      facade.invite('  a@example.com  ', 'role1', 'marketplace');

      expect(gateway.inviteUser).toHaveBeenCalledWith('a@example.com', 'role1', 'marketplace');
    });

    it('sets mutationError on failure', () => {
      gateway.inviteUser.and.returnValue(throwError(() => new Error('x')));

      facade.invite('a@example.com', 'role1', 'marketplace');

      expect(facade.mutationError()).toBe('common.errorDescription');
    });
  });

  describe('sessions dialog', () => {
    it('openSessions sets the target and loads that user\'s sessions', () => {
      gateway.loadSessions.and.returnValue(of([{ id: 's1' } as any]));

      facade.openSessions(user());

      expect(facade.sessionsTarget()?.id).toBe('u1');
      expect(facade.sessions().length).toBe(1);
    });

    it('closeSessions clears the target', () => {
      gateway.loadSessions.and.returnValue(of([]));
      facade.openSessions(user());

      facade.closeSessions();

      expect(facade.sessionsTarget()).toBeNull();
    });

    it('revokeSession reloads sessions for the currently open user, not a stale one', () => {
      gateway.loadSessions.and.returnValue(of([]));
      gateway.revokeSession.and.returnValue(of(undefined));
      facade.openSessions(user({ id: 'u1' }));

      facade.revokeSession('s1');

      expect(gateway.loadSessions).toHaveBeenCalledWith('u1');
    });

    it('revokeSession does nothing extra when no sessions dialog is open', () => {
      gateway.revokeSession.and.returnValue(of(undefined));

      facade.revokeSession('s1');

      expect(gateway.loadSessions).not.toHaveBeenCalled();
    });
  });

  describe('audit dialog', () => {
    it('openAudit loads entries for the target user', () => {
      gateway.loadAudit.and.returnValue(of([{ id: 'a1' } as any]));

      facade.openAudit(user());

      expect(facade.audit().length).toBe(1);
    });

    it('closeAudit clears the target', () => {
      gateway.loadAudit.and.returnValue(of([]));
      facade.openAudit(user());

      facade.closeAudit();

      expect(facade.auditTarget()).toBeNull();
    });
  });
});
