import {
  deepFreeze,
  descopeFeatureName,
  escapeDoubleQuotes,
  explodeSlug,
  extractMultilineFields,
  parseRepo,
  sortProjects,
  valueIsEqual,
} from 'sentry/utils';

describe('utils.valueIsEqual', function () {
  it('should return true when objects are deeply equal', function () {
    const isEqual = valueIsEqual(
      {
        username: 'foo',
        teams: ['bar', 'baz'],
        avatar: {
          avatarType: 'gravatar',
          avatarUuid: null,
        },
      },
      {
        username: 'foo',
        teams: ['bar', 'baz'],
        avatar: {
          avatarType: 'gravatar',
          avatarUuid: null,
        },
      },
      true
    );
    expect(isEqual).toBe(true);
  });

  it('should return false when objects are not deeply equal', function () {
    const isEqual = valueIsEqual(
      {
        username: 'foo',
        teams: ['bar', 'baz'],
        avatar: {
          avatarType: 'gravatar',
          avatarUuid: null,
        },
      },
      {
        username: 'foo',
        teams: ['bar', 'baz'],
        avatar: {
          avatarType: 'notGravatar',
          avatarUuid: null,
        },
      },
      true
    );
    expect(isEqual).toBe(false);
  });

  it('should return true when objects are shallowly equal', function () {
    const isEqual = valueIsEqual(
      {
        username: 'foo',
        team: 'bar',
        avatar: 'gravatar',
      },
      {
        username: 'foo',
        team: 'bar',
        avatar: 'gravatar',
      },
      false
    );
    expect(isEqual).toBe(true);
  });

  it('should return false when objects are not shallowly equal', function () {
    const isEqual = valueIsEqual(
      {
        username: 'foo',
        team: 'bar',
        avatar: 'gravatar',
      },
      {
        username: 'foo',
        team: 'bar',
        avatar: 'notGravatar',
      },
      false
    );
    expect(isEqual).toBe(false);
  });

  it('should not blow up when comparing null value to an object', function () {
    let isEqual = valueIsEqual(null, {username: 'foo'}, true);
    expect(isEqual).toBe(false);

    isEqual = valueIsEqual(
      {
        username: 'foo',
        teams: ['bar', 'baz'],
        avatar: null,
      },
      {
        username: 'foo',
        teams: ['bar', 'baz'],
        avatar: {
          avatarType: 'notGravatar',
          avatarUuid: null,
        },
      },
      true
    );
    expect(isEqual).toBe(false);
  });
});

describe('utils.extractMultilineFields', function () {
  it('should work for basic, simple values', function () {
    expect(extractMultilineFields('one\ntwo\nthree')).toEqual(['one', 'two', 'three']);
  });

  it('should return an empty array if only whitespace', function () {
    expect(extractMultilineFields('    \n    \n\n\n   \n')).toEqual([]);
  });

  it('should trim values and ignore empty lines', function () {
    expect(
      extractMultilineFields(
        `one
  two

three
        four

five`
      )
    ).toEqual(['one', 'two', 'three', 'four', 'five']);
  });
});

describe('utils.parseRepo', function () {
  it('should work for simple github url', function () {
    expect(parseRepo('github.com/example/example')).toEqual('example/example');
  });
  it('should work for full github url', function () {
    expect(parseRepo('https://github.com/example/example')).toEqual('example/example');
  });
  it('should work for trailing slash', function () {
    expect(parseRepo('https://github.com/example/example/')).toEqual('example/example');
  });
  it('should work for simple BitBucket url', function () {
    expect(parseRepo('bitbucket.org/example/example')).toEqual('example/example');
  });
  it('should work for full BitBucket url', function () {
    expect(parseRepo('https://bitbucket.org/example/example')).toEqual('example/example');
  });
  it('should work for trailing Bitbucket slash', function () {
    expect(parseRepo('https://bitbucket.org/example/example/')).toEqual(
      'example/example'
    );
  });
  it('should work for repo only', function () {
    expect(parseRepo('example/example')).toEqual('example/example');
  });
  it('should parse repo from url with extra info', function () {
    expect(parseRepo('github.com/example/example/commits/adsadsa')).toEqual(
      'example/example'
    );
  });
  it('should work for nothing passed', function () {
    expect(parseRepo()).toEqual();
  });
});

describe('utils.explodeSlug', function () {
  it('replaces slug special chars with whitespace', function () {
    expect(explodeSlug('test--slug__replace-')).toEqual('test slug replace');
  });
});

describe('utils.projectDisplayCompare', function () {
  it('sorts by bookmark and project slug', function () {
    const projects = [
      {
        isBookmarked: true,
        slug: 'm',
      },
      {
        isBookmarked: false,
        slug: 'm',
      },
      {
        isBookmarked: false,
        slug: 'a',
      },
      {
        isBookmarked: true,
        slug: 'a',
      },
      {
        isBookmarked: true,
        slug: 'z',
      },
      {
        isBookmarked: false,
        slug: 'z',
      },
    ];

    const sortedProjects = sortProjects(projects);

    expect(sortedProjects).toEqual([
      {
        isBookmarked: true,
        slug: 'a',
      },
      {
        isBookmarked: true,
        slug: 'm',
      },
      {
        isBookmarked: true,
        slug: 'z',
      },
      {
        isBookmarked: false,
        slug: 'a',
      },
      {
        isBookmarked: false,
        slug: 'm',
      },
      {
        isBookmarked: false,
        slug: 'z',
      },
    ]);
  });
});

describe('utils.descopeFeatureName', function () {
  it('descopes the feature name', () => {
    [
      ['organizations:feature', 'feature'],
      ['projects:feature', 'feature'],
      ['unknown-scope:feature', 'unknown-scope:feature'],
      ['', ''],
    ].forEach(([input, expected]) => expect(descopeFeatureName(input)).toEqual(expected));
  });
});

describe('deepFreeze', function () {
  it('throws error on attempt to mutate frozen object', function () {
    const testObj = deepFreeze({foo: [1, 2, 3]});

    [
      () => {
        testObj.foo.push(4);
      },
      () => {
        testObj.bar = '';
      },
      () => {
        delete testObj.foo;
      },
    ].forEach(fn => {
      expect(fn).toThrow();
    });
  });
});

describe('utils.escapeDoubleQuotes', function () {
  // test cases from https://gist.github.com/getify/3667624

  it('should escape any unescaped double quotes', function () {
    const cases = [
      ['a"b', 'a\\"b'], //
      ['a\\"b', 'a\\"b'], //
      ['a\\\\"b', 'a\\\\\\"b'],
      ['a"b"c', 'a\\"b\\"c'],
      ['a""b', 'a\\"\\"b'],
      ['""', '\\"\\"'],
    ];

    for (const testCase of cases) {
      const [input, expected] = testCase;
      expect(escapeDoubleQuotes(input)).toBe(expected);
    }

    // should return the same input as the output

    const cases2 = ['ab', 'a\\"b', 'a\\\\\\"b'];

    for (const test of cases2) {
      expect(escapeDoubleQuotes(test)).toBe(test);
    }

    // don't unnecessarily escape
    const actual = escapeDoubleQuotes(escapeDoubleQuotes(escapeDoubleQuotes('a"b')));
    expect(actual).toBe('a\\"b');
  });
});
