import { describe, expect, it } from 'vitest';
import { createUpdateGQLFragmentGenerator } from './UpdateGQLFragmentGenerator';

interface Mutation {
  createValue?: string;
}

enum Numbers {
  One = 'ONE',
  Two = 'TWO',
  Three = 'THREE',
}

interface Values {
  id: number;
  value?: string;
  number?: Numbers;
  nullable?: number | null | undefined;
  newValue?: {
    id?: number;
    value?: string;
    number?: number;
    nestedValue?: {
      nestedId?: number;
      nestedValue?: string;
    };
    nestedValue2?: {
      nestedId2?: number;
      deepNestedValue?: {
        deepNestedId?: number;
        deepNestedValue?: string;
      };
    };
  };
  relatedValue?: {
    id?: number;
    name?: string;
  };
}

interface CreateValueArgs {
  input: Values;
}

describe('createUpdateGQLFragmentGenerator', () => {
  it('creates an UpdateGQLFragmentGenerator function', () => {
    const UpdateGQLFragmentGenerator =
      createUpdateGQLFragmentGenerator<Mutation>();

    const mutation = UpdateGQLFragmentGenerator<CreateValueArgs>(
      'createValue',
      {
        input: {
          id: 1,
          value: 'test',
          number: { type: 'enum', value: Numbers.One },
          nullable: null,
        },
      },
    );

    expect(mutation).toBe(
      `createValue (input:{id:1,value:"test",number:ONE,nullable:null}) {clientMutationId}`,
    );
  });

  it('removes `"` from property names and escapes correctly values with `"` symbol', () => {
    const UpdateGQLFragmentGenerator =
      createUpdateGQLFragmentGenerator<Mutation>();
    // prettier-ignore
    const input = {"input":{ "name": "Hello world\":", "description": "Local Development Environment"}}
    const mutation = UpdateGQLFragmentGenerator<{
      input: { name: string; description: string };
    }>('createValue', input);
    expect(mutation).toBe(
      // prettier-ignore
      // eslint-disable-next-line no-useless-escape
      `createValue (input:{name:"Hello world\\\":",description:"Local Development Environment"}) {clientMutationId}`,
    );
  });

  describe('Results', () => {
    const defaultCreateValues: CreateValueArgs = {
      input: {
        id: 1,
      },
    };

    it(`defaults to 'clientMutationId'`, () => {
      const UpdateGQLFragmentGenerator =
        createUpdateGQLFragmentGenerator<Mutation>();

      const mutation = UpdateGQLFragmentGenerator<CreateValueArgs>(
        'createValue',
        defaultCreateValues,
      );

      expect(mutation).toBe(`createValue (input:{id:1}) {clientMutationId}`);
    });

    it('handles a single key', () => {
      const UpdateGQLFragmentGenerator =
        createUpdateGQLFragmentGenerator<Mutation>();

      const mutation = UpdateGQLFragmentGenerator<
        CreateValueArgs,
        { id: string }
      >('createValue', defaultCreateValues, ['id']);

      expect(mutation).toBe(`createValue (input:{id:1}) {id}`);
    });

    it('handles multiple keys', () => {
      const UpdateGQLFragmentGenerator =
        createUpdateGQLFragmentGenerator<Mutation>();

      const mutation = UpdateGQLFragmentGenerator<CreateValueArgs, Values>(
        'createValue',
        defaultCreateValues,
        ['id', 'value'],
      );

      expect(mutation).toBe(`createValue (input:{id:1}) {id value}`);
    });

    it('handles a single custom result', () => {
      const UpdateGQLFragmentGenerator =
        createUpdateGQLFragmentGenerator<Mutation>();

      const mutation = UpdateGQLFragmentGenerator<CreateValueArgs, Values>(
        'createValue',
        defaultCreateValues,
        [{ newValue: ['id', 'value', 'number'] }],
      );

      expect(mutation).toBe(
        `createValue (input:{id:1}) {newValue {id value number}}`,
      );
    });

    it('handles multiple custom results', () => {
      const UpdateGQLFragmentGenerator =
        createUpdateGQLFragmentGenerator<Mutation>();

      const mutation = UpdateGQLFragmentGenerator<CreateValueArgs, Values>(
        'createValue',
        defaultCreateValues,
        [{ newValue: ['id', 'value'] }, { relatedValue: ['id', 'name'] }],
      );

      expect(mutation).toBe(
        `createValue (input:{id:1}) {newValue {id value} relatedValue {id name}}`,
      );
    });

    it('handles nested custom results', () => {
      const UpdateGQLFragmentGenerator =
        createUpdateGQLFragmentGenerator<Mutation>();

      const mutation = UpdateGQLFragmentGenerator<CreateValueArgs, Values>(
        'createValue',
        defaultCreateValues,
        [
          {
            newValue: [
              'id',
              'value',
              { nestedValue: ['nestedId', 'nestedValue'] },
            ],
          },
        ],
      );

      expect(mutation).toBe(
        `createValue (input:{id:1}) {newValue {id value nestedValue {nestedId nestedValue}}}`,
      );
    });

    it('handles both a key and a custom result', () => {
      const UpdateGQLFragmentGenerator =
        createUpdateGQLFragmentGenerator<Mutation>();

      const mutation = UpdateGQLFragmentGenerator<CreateValueArgs, Values>(
        'createValue',
        defaultCreateValues,
        [
          'id',
          {
            newValue: [
              'id',
              'value',
              { nestedValue: ['nestedId', 'nestedValue'] },
            ],
          },
        ],
      );

      expect(mutation).toBe(
        `createValue (input:{id:1}) {id newValue {id value nestedValue {nestedId nestedValue}}}`,
      );
    });

    it('handles recursive', () => {
      const UpdateGQLFragmentGenerator =
        createUpdateGQLFragmentGenerator<Mutation>();

      const mutation = UpdateGQLFragmentGenerator<CreateValueArgs, Values>(
        'createValue',
        defaultCreateValues,
        [
          {
            newValue: [
              'id',
              'value',
              { nestedValue: ['nestedId', 'nestedValue'] },
              {
                nestedValue2: [
                  'nestedId2',
                  { deepNestedValue: ['deepNestedId', 'deepNestedValue'] },
                ],
              },
            ],
          },
        ],
      );

      expect(mutation).toBe(
        `createValue (input:{id:1}) {newValue {id value nestedValue {nestedId nestedValue} nestedValue2 {nestedId2 deepNestedValue {deepNestedId deepNestedValue}}}}`,
      );
    });
  });
});
