import { describe, expect, it } from 'vitest';
import { BulkEditConfig } from './BulkEdit.model';
import { generateBulkEditMutation } from './GenerateMutation';

describe('generateBulkEditMutation', () => {
  const BulkEditImagesAsyncFormFieldsConfig: BulkEditConfig = {
    mutation: 'bulkEditImagesAsync',
    fields: {
      imagesTagsAdd: {
        type: [
          {
            name: 'String!',
          },
        ],
        label: 'imagesTags ( Add )',
        originalFieldName: 'imagesTags',
        action: 'relatedEntitiesToAdd',
      },
      imagesTagsRemove: {
        type: [
          {
            name: 'String!',
          },
        ],
        label: 'imagesTags ( Remove )',
        originalFieldName: 'imagesTags',
        action: 'relatedEntitiesToRemove',
      },
      altText: {
        type: 'String',
        label: 'altText',
        originalFieldName: 'altText',
        action: 'set',
      },
      focalX: {
        type: 'BigFloat',
        label: 'focalX',
        originalFieldName: 'focalX',
        action: 'set',
      },
      focalY: {
        type: 'BigFloat',
        label: 'focalY',
        originalFieldName: 'focalY',
        action: 'set',
      },
      isArchived: {
        type: 'Boolean',
        label: 'isArchived',
        originalFieldName: 'isArchived',
        action: 'set',
      },
      title: {
        type: 'String',
        label: 'title',
        originalFieldName: 'title',
        action: 'set',
      },
    },
  };

  it('should generate a mutation string with all fields populated', () => {
    const sampleValues = {
      imagesTagsAdd: ['tag1', 'tag2'],
      imagesTagsRemove: ['tag3', 'tag4'],
      altText: 'Sample Alt Text',
      focalX: 0.5,
      focalY: 0.5,
      isArchived: false,
      title: 'Sample Title',
    };

    const result = generateBulkEditMutation(
      BulkEditImagesAsyncFormFieldsConfig,
      sampleValues,
    );

    expect(result).toBe(
      `mutation { bulkEditImagesAsync (relatedEntitiesToAdd: {imagesTags: [{name: "tag1"}, {name: "tag2"}]}, relatedEntitiesToRemove: {imagesTags: [{name: "tag3"}, {name: "tag4"}]}, set: {altText: "Sample Alt Text", focalX: 0.5, focalY: 0.5, isArchived: false, title: "Sample Title"}) { filterMatchedIds } }`,
    );
  });

  it('should generate a mutation string with only relatedEntitiesToAdd', () => {
    const sampleValues = {
      imagesTagsAdd: ['tag1', 'tag2'],
    };

    const result = generateBulkEditMutation(
      BulkEditImagesAsyncFormFieldsConfig,
      sampleValues,
    );

    expect(result).toBe(
      `mutation { bulkEditImagesAsync (relatedEntitiesToAdd: {imagesTags: [{name: "tag1"}, {name: "tag2"}]}) { filterMatchedIds } }`,
    );
  });

  it('should generate a mutation string with only relatedEntitiesToRemove', () => {
    const sampleValues = {
      imagesTagsRemove: ['tag3', 'tag4'],
    };

    const result = generateBulkEditMutation(
      BulkEditImagesAsyncFormFieldsConfig,
      sampleValues,
    );

    expect(result).toBe(
      `mutation { bulkEditImagesAsync (relatedEntitiesToRemove: {imagesTags: [{name: "tag3"}, {name: "tag4"}]}) { filterMatchedIds } }`,
    );
  });

  it('should generate a mutation string with only set fields', () => {
    const sampleValues = {
      altText: 'Sample Alt Text',
      focalX: 0.5,
      focalY: 0.5,
      isArchived: false,
      title: 'Sample Title',
    };

    const result = generateBulkEditMutation(
      BulkEditImagesAsyncFormFieldsConfig,
      sampleValues,
    );

    expect(result).toBe(
      `mutation { bulkEditImagesAsync (set: {altText: "Sample Alt Text", focalX: 0.5, focalY: 0.5, isArchived: false, title: "Sample Title"}) { filterMatchedIds } }`,
    );
  });

  it('should generate an empty mutation string if no values are provided', () => {
    const sampleValues = {};

    expect(() =>
      generateBulkEditMutation(
        BulkEditImagesAsyncFormFieldsConfig,
        sampleValues,
      ),
    ).toThrow('No valid fields to generate mutation');
  });

  it('should include filter parameter when provided', () => {
    const sampleValues = {
      altText: 'Sample Alt Text',
    };
    const filter = { id: { in: ['1', '2', '3'] } };

    const result = generateBulkEditMutation(
      BulkEditImagesAsyncFormFieldsConfig,
      sampleValues,
      filter,
    );

    expect(result).toBe(
      `mutation { bulkEditImagesAsync (set: {altText: "Sample Alt Text"}, filter: {id: {in: ["1", "2", "3"]}}) { filterMatchedIds } }`,
    );
  });

  it('should use custom key names when provided in config', () => {
    const configWithCustomKeys: BulkEditConfig = {
      mutation: 'bulkEditCustom',
      keys: {
        add: 'customAdd',
        remove: 'customRemove',
        clear: 'customClear',
        set: 'customSet',
        filter: 'customFilter',
      },
      fields: {
        tagsAdd: {
          type: [{ name: 'String!' }],
          label: 'Tags Add',
          originalFieldName: 'tags',
          action: 'customAdd',
        },
        tagsRemove: {
          type: [{ name: 'String!' }],
          label: 'Tags Remove',
          originalFieldName: 'tags',
          action: 'customRemove',
        },
        title: {
          type: 'String',
          label: 'Title',
          originalFieldName: 'title',
          action: 'customSet',
        },
      },
    };

    const sampleValues = {
      tagsAdd: ['tag1'],
      tagsRemove: ['tag2'],
      title: 'Custom Title',
    };
    const filter = { active: true };

    const result = generateBulkEditMutation(
      configWithCustomKeys,
      sampleValues,
      filter,
    );

    expect(result).toBe(
      `mutation { bulkEditCustom (customAdd: {tags: [{name: "tag1"}]}, customRemove: {tags: [{name: "tag2"}]}, customSet: {title: "Custom Title"}, customFilter: {active: true}) { filterMatchedIds } }`,
    );
  });

  it('should handle object values in related entities arrays', () => {
    const configWithObjectValues: BulkEditConfig = {
      mutation: 'bulkEditObjects',
      fields: {
        categoriesAdd: {
          type: [{ id: 'String!', name: 'String!' }],
          label: 'Categories Add',
          originalFieldName: 'categories',
          action: 'relatedEntitiesToAdd',
        },
      },
    };

    const sampleValues = {
      categoriesAdd: [
        { id: '1', name: 'Category 1' },
        { id: '2', name: 'Category 2' },
      ],
    };

    const result = generateBulkEditMutation(
      configWithObjectValues,
      sampleValues,
    );

    expect(result).toBe(
      `mutation { bulkEditObjects (relatedEntitiesToAdd: {categories: [{id: "1", name: "Category 1"}, {id: "2", name: "Category 2"}]}) { filterMatchedIds } }`,
    );
  });

  it('should handle null and undefined values correctly', () => {
    const sampleValues = {
      altText: null,
      focalX: undefined,
      title: '',
      isArchived: false,
    };

    const result = generateBulkEditMutation(
      BulkEditImagesAsyncFormFieldsConfig,
      sampleValues,
    );

    // Note: null and undefined values are excluded by the implementation's fieldValues !== undefined && fieldValues !== null check
    // Only non-null/undefined values are included in the 'set' operation
    expect(result).toBe(
      `mutation { bulkEditImagesAsync (set: {isArchived: false, title: ""}) { filterMatchedIds } }`,
    );
  });

  it('should include null values explicitly in set operations when they are the only value', () => {
    const sampleValues = {
      altText: null,
    };

    // This test demonstrates the current behavior - null values are excluded
    // This might be a bug in the implementation, as setting a field to null might be intentional
    expect(() =>
      generateBulkEditMutation(
        BulkEditImagesAsyncFormFieldsConfig,
        sampleValues,
      ),
    ).toThrow('No valid fields to generate mutation');
  });

  it('should handle zero and false values correctly (they should be included)', () => {
    const sampleValues = {
      focalX: 0,
      focalY: 0,
      isArchived: false,
      title: '',
    };

    const result = generateBulkEditMutation(
      BulkEditImagesAsyncFormFieldsConfig,
      sampleValues,
    );

    expect(result).toBe(
      `mutation { bulkEditImagesAsync (set: {focalX: 0, focalY: 0, isArchived: false, title: ""}) { filterMatchedIds } }`,
    );
  });

  it('should handle empty arrays in related entities', () => {
    const sampleValues = {
      imagesTagsAdd: [],
      imagesTagsRemove: ['tag1'],
      altText: 'Test',
    };

    const result = generateBulkEditMutation(
      BulkEditImagesAsyncFormFieldsConfig,
      sampleValues,
    );

    // Note: Empty arrays are truthy, so they get included in the mutation
    expect(result).toBe(
      `mutation { bulkEditImagesAsync (relatedEntitiesToAdd: {imagesTags: []}, relatedEntitiesToRemove: {imagesTags: [{name: "tag1"}]}, set: {altText: "Test"}) { filterMatchedIds } }`,
    );
  });

  it('should handle mixed string and object values in the same field', () => {
    const configWithMixedValues: BulkEditConfig = {
      mutation: 'bulkEditMixed',
      fields: {
        itemsAdd: {
          type: [{ id: 'String!' }],
          label: 'Items Add',
          originalFieldName: 'items',
          action: 'relatedEntitiesToAdd',
        },
      },
    };

    const sampleValues = {
      itemsAdd: ['string-id', { id: 'object-id', extra: 'data' }],
    };

    const result = generateBulkEditMutation(
      configWithMixedValues,
      sampleValues,
    );

    expect(result).toBe(
      `mutation { bulkEditMixed (relatedEntitiesToAdd: {items: [{id: "string-id"}, {id: "object-id", extra: "data"}]}) { filterMatchedIds } }`,
    );
  });

  it('should throw error when all provided values are falsy but not explicitly null/false', () => {
    const sampleValues = {
      imagesTagsAdd: undefined,
      imagesTagsRemove: null,
      altText: undefined,
    };

    expect(() =>
      generateBulkEditMutation(
        BulkEditImagesAsyncFormFieldsConfig,
        sampleValues,
      ),
    ).toThrow('No valid fields to generate mutation');
  });

  it('should handle clearValue for relatedEntitiesToClear', () => {
    const configWithClearable: BulkEditConfig = {
      mutation: 'bulkEditWithClear',
      fields: {
        tagsRemove: {
          type: [{ name: 'String!' }],
          label: 'Tags Remove',
          originalFieldName: 'tags',
          action: 'relatedEntitiesToRemove',
          clearable: true,
          clearValue: '__CLEAR_ALL__',
        },
        title: {
          type: 'String',
          label: 'Title',
          originalFieldName: 'title',
          action: 'set',
        },
      },
    };

    const sampleValues = {
      tagsRemove: '__CLEAR_ALL__',
      title: 'Test Title',
    };

    const result = generateBulkEditMutation(configWithClearable, sampleValues);

    expect(result).toBe(
      `mutation { bulkEditWithClear (relatedEntitiesToClear: {tags: true}, set: {title: "Test Title"}) { filterMatchedIds } }`,
    );
  });

  it('should use custom clear key when provided in config', () => {
    const configWithCustomClearKey: BulkEditConfig = {
      mutation: 'bulkEditCustomClear',
      keys: {
        add: 'relatedEntitiesToAdd',
        remove: 'relatedEntitiesToRemove',
        clear: 'customClear',
        set: 'set',
        filter: 'filter',
      },
      fields: {
        categoriesRemove: {
          type: [{ id: 'String!' }],
          label: 'Categories Remove',
          originalFieldName: 'categories',
          action: 'relatedEntitiesToRemove',
          clearable: true,
          clearValue: 'CLEAR',
        },
      },
    };

    const sampleValues = {
      categoriesRemove: 'CLEAR',
    };

    const result = generateBulkEditMutation(
      configWithCustomClearKey,
      sampleValues,
    );

    expect(result).toBe(
      `mutation { bulkEditCustomClear (customClear: {categories: true}) { filterMatchedIds } }`,
    );
  });

  it('should skip adding to remove array when clearValue is provided', () => {
    const configWithClearable: BulkEditConfig = {
      mutation: 'bulkEditSkipRemove',
      fields: {
        tagsRemove: {
          type: [{ name: 'String!' }],
          label: 'Tags Remove',
          originalFieldName: 'tags',
          action: 'relatedEntitiesToRemove',
          clearable: true,
          clearValue: '__CLEAR_ALL__',
        },
      },
    };

    const sampleValues = {
      tagsRemove: '__CLEAR_ALL__',
    };

    const result = generateBulkEditMutation(configWithClearable, sampleValues);

    // Should use relatedEntitiesToClear instead of relatedEntitiesToRemove
    expect(result).toBe(
      `mutation { bulkEditSkipRemove (relatedEntitiesToClear: {tags: true}) { filterMatchedIds } }`,
    );
  });

  it('should handle both regular remove values and clearValue in different fields', () => {
    const configWithMixedRemove: BulkEditConfig = {
      mutation: 'bulkEditMixedRemove',
      fields: {
        tagsRemove: {
          type: [{ name: 'String!' }],
          label: 'Tags Remove',
          originalFieldName: 'tags',
          action: 'relatedEntitiesToRemove',
          clearable: true,
          clearValue: '__CLEAR_ALL__',
        },
        categoriesRemove: {
          type: [{ id: 'String!' }],
          label: 'Categories Remove',
          originalFieldName: 'categories',
          action: 'relatedEntitiesToRemove',
        },
      },
    };

    const sampleValues = {
      tagsRemove: '__CLEAR_ALL__',
      categoriesRemove: [{ id: 'cat1' }, { id: 'cat2' }],
    };

    const result = generateBulkEditMutation(
      configWithMixedRemove,
      sampleValues,
    );

    expect(result).toBe(
      `mutation { bulkEditMixedRemove (relatedEntitiesToRemove: {categories: [{id: "cat1"}, {id: "cat2"}]}, relatedEntitiesToClear: {tags: true}) { filterMatchedIds } }`,
    );
  });
});
