/**
 * C-08: Distributed lock fail-closed behaviour
 * Ensures that acquireLock returns null (not a lock value) when Redis is unavailable.
 */

// We spy on the private redis instance fields to simulate availability/unavailability.

const mockClientSet = jest.fn();

// Track the redis instance created inside distributedLock module.
let capturedInstance: any;

jest.mock('#services/storage/redisService', () => {
	capturedInstance = {
		client: null,
		isConnected: false,
	};
	return {
		__esModule: true,
		default: { getInstance: jest.fn(() => capturedInstance) },
	};
});

import { acquireLock, releaseLock } from '../../../src/utils/distributedLock';

describe('acquireLock (C-08: fail-closed)', () => {
	beforeEach(() => {
		mockClientSet.mockReset();
		capturedInstance.client = null;
		capturedInstance.isConnected = false;
	});

	it('returns null when redis client is null (C-08 fail-closed)', async () => {
		capturedInstance.client = null;
		capturedInstance.isConnected = false;
		const result = await acquireLock('test-key', 60, 'my-lock');
		expect(result).toBeNull();
	});

	it('returns null when redis is not connected (C-08 fail-closed)', async () => {
		capturedInstance.client = {};
		capturedInstance.isConnected = false;
		const result = await acquireLock('test-key', 60, 'my-lock');
		expect(result).toBeNull();
	});

	it('returns lock value when redis SET NX succeeds', async () => {
		capturedInstance.client = { set: mockClientSet };
		capturedInstance.isConnected = true;
		mockClientSet.mockResolvedValue('OK');
		const result = await acquireLock('test-key', 60, 'my-lock');
		expect(result).toBe('my-lock');
		expect(mockClientSet).toHaveBeenCalledWith('lock:test-key', 'my-lock', 'NX', 'EX', 60);
	});

	it('returns null when lock already held (SET NX returns null)', async () => {
		capturedInstance.client = { set: mockClientSet };
		capturedInstance.isConnected = true;
		mockClientSet.mockResolvedValue(null);
		const result = await acquireLock('test-key', 60, 'my-lock');
		expect(result).toBeNull();
	});

	it('returns null (fail-closed) when redis.set throws an error', async () => {
		capturedInstance.client = { set: mockClientSet };
		capturedInstance.isConnected = true;
		mockClientSet.mockRejectedValue(new Error('Redis connection lost'));
		const result = await acquireLock('test-key', 60, 'my-lock');
		expect(result).toBeNull();
	});
});
