import { Test } from '@nestjs/testing';
import { LaravelIdentityService } from './laravel-identity.service';
import { LaravelApiService } from '../../laravel-api/laravel-api.service';

describe('LaravelIdentityService', () => {
  let service: LaravelIdentityService;
  const laravelApi = { makeAuthenticatedRequest: jest.fn() };

  beforeEach(async () => {
    jest.clearAllMocks();
    const module = await Test.createTestingModule({
      providers: [
        LaravelIdentityService,
        { provide: LaravelApiService, useValue: laravelApi },
      ],
    }).compile();
    service = module.get(LaravelIdentityService);
  });

  it('canUserAccessClient returns true on 200 and caches by token+uuid', async () => {
    laravelApi.makeAuthenticatedRequest.mockResolvedValue({
      status: 200,
      data: { can_access: true },
    });
    expect(await service.canUserAccessClient('tok-a', 'uuid-1')).toBe(true);
    expect(await service.canUserAccessClient('tok-a', 'uuid-1')).toBe(true);
    expect(laravelApi.makeAuthenticatedRequest).toHaveBeenCalledTimes(1); // cached
    expect(laravelApi.makeAuthenticatedRequest).toHaveBeenCalledWith('tok-a', {
      method: 'GET',
      endpoint: '/v1/user/client/uuid-1/can-access',
    });
  });

  it('canUserAccessClient returns false on error/404 and does NOT cache failures', async () => {
    laravelApi.makeAuthenticatedRequest.mockRejectedValue(new Error('404'));
    expect(await service.canUserAccessClient('tok-b', 'uuid-2')).toBe(false);
    laravelApi.makeAuthenticatedRequest.mockResolvedValue({
      status: 200,
      data: { can_access: true },
    });
    expect(await service.canUserAccessClient('tok-b', 'uuid-2')).toBe(true); // retried
  });

  it('resolveMe returns identity fields and caches', async () => {
    laravelApi.makeAuthenticatedRequest.mockResolvedValue({
      status: 200,
      data: { uuid: 'u-1', stripe_id: 'cus_123' },
    });
    expect(await service.resolveMe('tok-c')).toEqual({
      uuid: 'u-1',
      stripe_id: 'cus_123',
    });
    await service.resolveMe('tok-c');
    expect(laravelApi.makeAuthenticatedRequest).toHaveBeenCalledTimes(1);
    expect(laravelApi.makeAuthenticatedRequest).toHaveBeenCalledWith('tok-c', {
      method: 'GET',
      endpoint: '/v1/me',
      params: { fields: 'uuid,stripe_id' },
    });
  });

  it('never stores the raw token as a cache key', async () => {
    laravelApi.makeAuthenticatedRequest.mockResolvedValue({
      status: 200,
      data: { can_access: true },
    });
    await service.canUserAccessClient('super-secret-token', 'uuid-9');
    const keys = [...(service as any).cache.keys()].join('|');
    expect(keys).not.toContain('super-secret-token');
  });

  it('canUserAccessClient returns false immediately for null/empty token', async () => {
    expect(await service.canUserAccessClient('', 'uuid-x')).toBe(false);
    expect(laravelApi.makeAuthenticatedRequest).not.toHaveBeenCalled();
  });

  it('resolveMe returns null immediately for null/empty token', async () => {
    expect(await service.resolveMe('')).toBeNull();
    expect(laravelApi.makeAuthenticatedRequest).not.toHaveBeenCalled();
  });

  it('caps the cache size', async () => {
    laravelApi.makeAuthenticatedRequest.mockResolvedValue({
      status: 200,
      data: { can_access: true },
    });
    for (let i = 0; i < 5100; i++) {
      await service.canUserAccessClient(`tok-${i}`, `uuid-${i}`);
    }
    expect((service as any).cache.size).toBeLessThanOrEqual(5000);
  });
});
