import { FieldValueTypes, OperatorTypes, Semantics } from '@unito/integration-api';

import assert from 'node:assert/strict';
import { describe, it } from 'node:test';
import { buildCollectionQueryParams, getApplicableFilters } from '../src/helpers.js';

describe('Helpers', () => {
  describe('getApplicableFilters', () => {
    it('returns only filters for defined fields', () => {
      const actual = getApplicableFilters(
        {
          filters: [
            { field: 'status', operator: OperatorTypes.EQUAL, values: ['active', 'pending'] },
            { field: 'email', operator: OperatorTypes.IS_NOT_NULL, values: [] },
          ],
        },
        [
          {
            name: 'status',
            label: 'Status',
            type: FieldValueTypes.STRING,
          },
        ],
      );

      const expected = [{ field: 'status', operator: OperatorTypes.EQUAL, values: ['active', 'pending'] }];

      assert.deepEqual(actual, expected);
    });

    it('translates semantics into field names', () => {
      const actual = getApplicableFilters(
        {
          filters: [
            { field: 'semantic:displayName', operator: OperatorTypes.START_WITH, values: ['Bob'] },
            { field: 'semantic:createdAt', operator: OperatorTypes.EQUAL, values: ['2021-01-01'] },
          ],
        },
        [
          {
            name: 'name',
            label: 'Name',
            type: FieldValueTypes.STRING,
            semantic: Semantics.DISPLAY_NAME,
          },
        ],
      );

      const expected = [{ field: 'name', operator: OperatorTypes.START_WITH, values: ['Bob'] }];

      assert.deepEqual(actual, expected);
    });

    it('gracefully handle garbage', () => {
      const actual = getApplicableFilters(
        {
          filters: [
            { field: '...', operator: OperatorTypes.EQUAL, values: [] },
            { field: ':', operator: OperatorTypes.EQUAL, values: [] },
            { field: '', operator: OperatorTypes.EQUAL, values: [] },
          ],
        },
        [
          {
            name: 'status',
            label: 'Status',
            type: FieldValueTypes.STRING,
          },
        ],
      );

      assert.deepEqual(actual, []);
    });
  });

  describe('buildCollectionQueryParams', () => {
    it('encodes a single filter with one value', () => {
      const result = buildCollectionQueryParams({
        filters: [{ field: 'status', operator: OperatorTypes.EQUAL, values: ['active'] }],
      });
      assert.equal(result.get('filter'), 'status=active');
      assert.equal(result.get('select'), null);
    });

    it('encodes filter values with special characters', () => {
      const result = buildCollectionQueryParams({
        filters: [{ field: 'status', operator: OperatorTypes.EQUAL, values: ['foo,bar'] }],
      });
      assert.equal(result.get('filter'), 'status=foo%2Cbar');
    });

    it('encodes filter values containing pipe characters', () => {
      const result = buildCollectionQueryParams({
        filters: [{ field: 'tags', operator: OperatorTypes.EQUAL, values: ['a|b'] }],
      });
      assert.equal(result.get('filter'), 'tags=a%7Cb');
    });

    it('joins multiple values for one filter with |', () => {
      const result = buildCollectionQueryParams({
        filters: [{ field: 'status', operator: OperatorTypes.EQUAL, values: ['active', 'pending'] }],
      });
      assert.equal(result.get('filter'), 'status=active|pending');
    });

    it('joins multiple filters with ,', () => {
      const result = buildCollectionQueryParams({
        filters: [
          { field: 'status', operator: OperatorTypes.EQUAL, values: ['active'] },
          { field: 'name', operator: OperatorTypes.INCLUDE, values: ['john'] },
        ],
      });
      assert.equal(result.get('filter'), 'status=active,name*=john');
    });

    it('handles IS_NULL operator (no values)', () => {
      const result = buildCollectionQueryParams({
        filters: [{ field: 'status', operator: OperatorTypes.IS_NULL, values: [] }],
      });
      assert.equal(result.get('filter'), 'status!!');
    });

    it('handles IS_NULL operator with undefined values', () => {
      const result = buildCollectionQueryParams({
        filters: [{ field: 'status', operator: OperatorTypes.IS_NULL, values: undefined }],
      });
      assert.equal(result.get('filter'), 'status!!');
    });

    it('handles IS_NOT_NULL operator (no values)', () => {
      const result = buildCollectionQueryParams({
        filters: [{ field: 'assignee', operator: OperatorTypes.IS_NOT_NULL, values: [] }],
      });
      assert.equal(result.get('filter'), 'assignee!');
    });

    it('encodes selects', () => {
      const result = buildCollectionQueryParams({
        selects: ['name', 'dept.id'],
      });
      assert.equal(result.get('select'), 'name,dept.id');
      assert.equal(result.get('filter'), null);
    });

    it('encodes selects with special characters', () => {
      const result = buildCollectionQueryParams({
        selects: ['field with spaces'],
      });
      assert.equal(result.get('select'), 'field%20with%20spaces');
    });

    it('encodes both filters and selects', () => {
      const result = buildCollectionQueryParams({
        filters: [{ field: 'status', operator: OperatorTypes.EQUAL, values: ['active'] }],
        selects: ['name', 'status'],
      });
      assert.equal(result.get('filter'), 'status=active');
      assert.equal(result.get('select'), 'name,status');
    });

    it('returns empty URLSearchParams when called with no arguments', () => {
      const result = buildCollectionQueryParams();
      assert.equal(result.toString(), '');
    });

    it('omits filter key for empty filters array', () => {
      const result = buildCollectionQueryParams({ filters: [] });
      assert.equal(result.get('filter'), null);
    });

    it('omits select key for empty selects array', () => {
      const result = buildCollectionQueryParams({ selects: [] });
      assert.equal(result.get('select'), null);
    });

    it('can be extended with additional params (e.g. cursor)', () => {
      const result = buildCollectionQueryParams({
        selects: ['name'],
      });
      result.append('cursor', 'abc123');
      assert.equal(result.toString(), 'select=name&cursor=abc123');
    });
  });
});
