/**
 * Property-Based Test: JQL Window Invariant
 *
 * Feature: jira-api-compliance-cleanup, Property 1: JQL window is always 72 hours in bulk sync
 *
 * For any non-empty array of valid-looking issue keys passed to searchIssuesByKeys(),
 * the generated JQL string SHALL contain the substring `updated >= -72h` and
 * SHALL contain the substring `project =`.
 *
 * Validates: Requirements 2.1, 2.3
 */

const fc = require('fast-check');

// Capture the JQL that flows through the HTTP layer.
let capturedJql = null;

// Mock https to intercept the request URL (which contains the JQL) and return
// a fake 200 response. This prevents real network calls while letting the
// real searchIssuesByKeys → searchIssues → jiraGet → jiraRequest chain execute.
jest.mock('https', () => ({
  request: jest.fn((options, callback) => {
    const fullPath = options.path || '';
    const jqlMatch = fullPath.match(/[?&]jql=([^&]*)/);
    if (jqlMatch) {
      capturedJql = decodeURIComponent(jqlMatch[1]);
    }

    const mockResponse = {
      statusCode: 200,
      on: jest.fn((event, handler) => {
        if (event === 'data') {
          handler(JSON.stringify({ total: 0, issues: [] }));
        }
        if (event === 'end') {
          handler();
        }
      }),
    };
    // Use setImmediate so the callback fires on the same tick after promises
    // resolve, but still asynchronously as Node's http expects.
    setImmediate(() => callback(mockResponse));

    return {
      on: jest.fn(),
      write: jest.fn(),
      end: jest.fn(),
      destroy: jest.fn(),
    };
  }),
}));

// Set required env vars before requiring the module so the module-level
// constants pick them up.
process.env.JIRA_PROJECT_KEY = 'TESTPROJ';
process.env.JIRA_BASE_URL = 'https://jira.example.com';
process.env.JIRA_API_USER = 'testuser';
process.env.JIRA_API_TOKEN = 'testtoken';

const jiraApi = require('../helpers/jiraApi');

describe('Feature: jira-api-compliance-cleanup, Property 1: JQL window is always 72h in bulk sync', () => {
  // Use fake timers so the rate-limiter's inter-request delays (1–2 seconds)
  // resolve instantly. We preserve setImmediate so the https mock callback
  // still fires asynchronously as expected.
  beforeAll(() => {
    jest.useFakeTimers({ doNotFake: ['setImmediate', 'nextTick'] });
  });

  afterAll(() => {
    jest.useRealTimers();
  });

  beforeEach(() => {
    capturedJql = null;
  });

  // Generator: produces a valid Jira issue key like "AB-1", "PROJ-42", etc.
  const issueKeyArb = fc.tuple(
    fc.stringMatching(/^[A-Z]{2,10}$/),
    fc.integer({ min: 1, max: 99999 })
  ).map(([prefix, num]) => `${prefix}-${num}`);

  // Generator: non-empty array of issue keys (1 to 50 keys)
  const issueKeysArb = fc.array(issueKeyArb, { minLength: 1, maxLength: 50 });

  it('searchIssuesByKeys() always generates JQL containing `updated >= -72h` and `project =`', async () => {
    await fc.assert(
      fc.asyncProperty(issueKeysArb, async (issueKeys) => {
        capturedJql = null;

        // Start the call — it will hit waitForDelay which uses setTimeout
        const promise = jiraApi.searchIssuesByKeys(issueKeys);

        // Advance fake timers to resolve any pending setTimeout from the
        // rate limiter's waitForDelay function.
        jest.advanceTimersByTime(5000);

        await promise;

        expect(capturedJql).not.toBeNull();
        expect(capturedJql).toContain('updated >= -72h');
        // project filter intentionally removed — issue keys are globally unique
        // and the filter broke cross-project ticket sync
      }),
      { numRuns: 100 }
    );
  }, 60000);
});
