/**
 * Property-Based Tests: FP Submissions Cleanup
 *
 * Feature: fp-submissions-cleanup
 *
 * Tests the pure filtering functions used to determine which FP submissions
 * are visible in the Queue Panel and which show the dismiss button.
 *
 * Validates: Requirements 1.1, 2.1, 2.2, 2.3
 */

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

// Mock db pool before importing the route module (avoids DATABASE_URL requirement)
jest.mock('../db', () => ({
    query: jest.fn(() => Promise.resolve({ rows: [], rowCount: 0 })),
}));

// Mock dependencies that the route module imports
jest.mock('../helpers/auditLog', () => jest.fn());
jest.mock('../helpers/ivantiApi', () => ({
    ivantiFormPost: jest.fn(),
    ivantiPost: jest.fn(),
}));

const { filterVisibleSubmissions, shouldShowDismissButton } = require('../routes/ivantiFpWorkflow');

// --- Generators ---

const lifecycleStatusArb = fc.constantFrom('submitted', 'approved', 'rejected', 'rework', 'resubmitted');

const dismissedAtArb = fc.oneof(
    fc.constant(null),
    fc.date({ min: new Date('2020-01-01T00:00:00.000Z'), max: new Date('2030-12-31T00:00:00.000Z') })
        .filter(d => !isNaN(d.getTime()))
        .map(d => d.toISOString())
);

const submissionArb = fc.record({
    id: fc.integer({ min: 1, max: 100000 }),
    lifecycle_status: lifecycleStatusArb,
    dismissed_at: dismissedAtArb,
    user_id: fc.integer({ min: 1, max: 1000 }),
    ivanti_workflow_batch_id: fc.string({ minLength: 1, maxLength: 20 })
});

const submissionsArrayArb = fc.array(submissionArb, { minLength: 0, maxLength: 50 });

// --- Property 1: Submission Visibility Filter ---

describe('Feature: fp-submissions-cleanup, Property 1: Submission Visibility Filter', () => {
    /**
     * For any array of FP submission objects with arbitrary lifecycle_status values
     * and arbitrary dismissed_at values, filterVisibleSubmissions(submissions) should
     * return only submissions where lifecycle_status is NOT "approved" AND dismissed_at
     * is null. Additionally, every submission in the input that satisfies both conditions
     * must appear in the output, and the output length must be <= input length.
     *
     * Validates: Requirements 1.1, 2.2, 2.3
     */
    it('returns only non-approved and non-dismissed submissions', () => {
        fc.assert(
            fc.property(submissionsArrayArb, (submissions) => {
                const result = filterVisibleSubmissions(submissions);

                // Output length must be <= input length
                expect(result.length).toBeLessThanOrEqual(submissions.length);

                // Every item in the result must be non-approved and non-dismissed
                for (const s of result) {
                    expect(s.lifecycle_status).not.toBe('approved');
                    expect(s.dismissed_at).toBeNull();
                }

                // Every input item that satisfies both conditions must appear in the output
                const expected = submissions.filter(
                    s => s.lifecycle_status !== 'approved' && s.dismissed_at == null
                );
                expect(result).toEqual(expected);
            }),
            { numRuns: 100 }
        );
    });
});

// --- Property 2: Dismiss Button Visibility Predicate ---

describe('Feature: fp-submissions-cleanup, Property 2: Dismiss Button Visibility Predicate', () => {
    /**
     * For any FP submission object with a lifecycle_status value drawn from
     * {submitted, approved, rejected, rework, resubmitted} and a dismissed_at value
     * (null or timestamp), the dismiss button should be rendered if and only if
     * lifecycle_status === 'rejected' AND dismissed_at is null.
     *
     * Validates: Requirements 2.1
     */
    it('returns true iff status is rejected and dismissed_at is null', () => {
        fc.assert(
            fc.property(submissionArb, (submission) => {
                const result = shouldShowDismissButton(submission);
                const expected = submission.lifecycle_status === 'rejected' && submission.dismissed_at == null;

                expect(result).toBe(expected);
            }),
            { numRuns: 100 }
        );
    });
});
