'use strict';

const common = require('../common');
const { describe, it } = require('node:test');
const assert = require('node:assert');
const { Worker } = require('worker_threads');

describe('Web Locks - query missing WPT tests', () => {
  it('should report different ids for held locks from different contexts', async () => {
    const worker = new Worker(`
      const { parentPort } = require('worker_threads');
      
      navigator.locks.request('different-contexts-resource', { mode: 'shared' }, async (lock) => {
        const state = await navigator.locks.query();
        const heldLocks = state.held.filter(l => l.name === 'different-contexts-resource');
        
        parentPort.postMessage({ clientId: heldLocks[0].clientId });
        
        await new Promise(resolve => {
          parentPort.once('message', () => resolve());
        });
      }).catch(err => parentPort.postMessage({ error: err.message }));
    `, { eval: true });

    const workerResult = await new Promise((resolve) => {
      worker.once('message', resolve);
    });

    await navigator.locks.request('different-contexts-resource', { mode: 'shared' }, common.mustCall(async (lock) => {
      const state = await navigator.locks.query();
      const heldLocks = state.held.filter((l) => l.name === 'different-contexts-resource');

      const mainClientId = heldLocks[0].clientId;

      assert.notStrictEqual(mainClientId, workerResult.clientId);

      worker.postMessage('release');
    }));

    await worker.terminate();
  });

  it('should observe a deadlock scenario', async () => {
    const worker = new Worker(`
      const { parentPort } = require('worker_threads');
      
      navigator.locks.request('deadlock-resource-1', async (lock1) => {
        parentPort.postMessage({ acquired: 'resource1' });
        
        await new Promise(resolve => {
          parentPort.once('message', () => resolve());
        });
        
        const result = await navigator.locks.request('deadlock-resource-2', 
          { ifAvailable: true }, (lock2) => lock2 !== null);
        
        parentPort.postMessage({ acquired2: result });
        
        await new Promise(resolve => {
          parentPort.once('message', () => resolve());
        });
      }).catch(err => parentPort.postMessage({ error: err.message }));
    `, { eval: true });

    const step1 = await new Promise((resolve) => {
      worker.once('message', resolve);
    });
    assert.strictEqual(step1.acquired, 'resource1');

    await navigator.locks.request('deadlock-resource-2', common.mustCall(async (lock2) => {
      worker.postMessage('try-resource2');

      const step2 = await new Promise((resolve) => {
        worker.once('message', resolve);
      });
      assert.strictEqual(step2.acquired2, false);

      const canGetResource1 = await navigator.locks.request('deadlock-resource-1',
                                                            { ifAvailable: true }, (lock1) => lock1 !== null);

      assert.strictEqual(canGetResource1, false);

      const state = await navigator.locks.query();
      const resource2Lock = state.held.find((l) => l.name === 'deadlock-resource-2');
      assert(resource2Lock);

      worker.postMessage('release');
    }));

    await worker.terminate();
  });
});
