/**
 * @license
 * Copyright 2020 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import '../test/common-test-setup';
import {
  isUnresolved,
  getPatchRangeForCommentUrl,
  createCommentThreads,
  sortComments,
  USER_SUGGESTION_START_PATTERN,
  hasUserSuggestion,
  getUserSuggestion,
  getContentInCommentRange,
  createUserFixSuggestion,
  PROVIDED_FIX_ID,
  getMentionedThreads,
} from './comment-util';
import {
  createAccountWithEmail,
  createComment,
  createCommentThread,
} from '../test/test-data-generators';
import {CommentSide} from '../constants/constants';
import {
  PARENT,
  RevisionPatchSetNum,
  Timestamp,
  UrlEncodedCommentId,
} from '../types/common';
import {assert} from '@open-wc/testing';

suite('comment-util', () => {
  test('isUnresolved', () => {
    const thread = createCommentThread([createComment()]);

    assert.isFalse(isUnresolved(thread));

    assert.isTrue(
      isUnresolved({
        ...thread,
        comments: [{...createComment(), unresolved: true}],
      })
    );
    assert.isFalse(
      isUnresolved({
        ...thread,
        comments: [{...createComment(), unresolved: false}],
      })
    );
    assert.isTrue(
      isUnresolved({
        ...thread,
        comments: [
          {...createComment(), unresolved: false},
          {...createComment(), unresolved: true},
        ],
      })
    );
    assert.isFalse(
      isUnresolved({
        ...thread,
        comments: [
          {...createComment(), unresolved: true},
          {...createComment(), unresolved: false},
        ],
      })
    );
  });

  suite('getPatchRangeForCommentUrl', () => {
    test('comment created with side=PARENT does not navigate to latest ps', () => {
      const comment = {
        ...createComment(),
        id: 'c4' as UrlEncodedCommentId,
        line: 10,
        patch_set: 4 as RevisionPatchSetNum,
        side: CommentSide.PARENT,
        path: '/COMMIT_MSG',
      };
      assert.deepEqual(
        getPatchRangeForCommentUrl(comment, 11 as RevisionPatchSetNum),
        {
          basePatchNum: PARENT,
          patchNum: 4 as RevisionPatchSetNum,
        }
      );
    });
  });

  test('getMentionedThreads', () => {
    const account = createAccountWithEmail('abcd@def.com');
    const threads = [
      createCommentThread([
        {
          ...createComment(),
          message: 'random text with no emails',
        },
      ]),
      createCommentThread([
        {
          ...createComment(),
          message: '@abcd@def.com please take a look',
        },
        {
          ...createComment(),
          message: '@abcd@def.com please take a look again at this',
        },
      ]),
      createCommentThread([
        {
          ...createComment(),
          message: '@abcd@def.com this is important',
        },
      ]),
    ];
    assert.deepEqual(getMentionedThreads(threads, account), [
      threads[1],
      threads[2],
    ]);

    assert.deepEqual(
      getMentionedThreads(threads, createAccountWithEmail('xyz@def.com')),
      []
    );
  });

  test('comments sorting', () => {
    const comments = [
      {
        id: 'new_draft' as UrlEncodedCommentId,
        message: 'i do not like either of you',
        __draft: true,
        updated: '2015-12-20 15:01:20.396000000' as Timestamp,
      },
      {
        id: 'sallys_confession' as UrlEncodedCommentId,
        message: 'i like you, jack',
        updated: '2015-12-23 15:00:20.396000000' as Timestamp,
        line: 1,
      },
      {
        id: 'jacks_reply' as UrlEncodedCommentId,
        message: 'i like you, too',
        updated: '2015-12-24 15:01:20.396000000' as Timestamp,
        line: 1,
        in_reply_to: 'sallys_confession',
      },
    ];
    const sortedComments = sortComments(comments);
    assert.equal(sortedComments[0], comments[1]);
    assert.equal(sortedComments[1], comments[2]);
    assert.equal(sortedComments[2], comments[0]);
  });

  suite('createCommentThreads', () => {
    test('creates threads from individual comments', () => {
      const comments = [
        {
          id: 'sallys_confession' as UrlEncodedCommentId,
          message: 'i like you, jack',
          updated: '2015-12-23 15:00:20.396000000' as Timestamp,
          line: 1,
          patch_set: 1 as RevisionPatchSetNum,
          path: 'some/path',
        },
        {
          id: 'jacks_reply' as UrlEncodedCommentId,
          message: 'i like you, too',
          updated: '2015-12-24 15:01:20.396000000' as Timestamp,
          line: 1,
          in_reply_to: 'sallys_confession' as UrlEncodedCommentId,
          patch_set: 1 as RevisionPatchSetNum,
          path: 'some/path',
        },
        {
          id: 'new_draft' as UrlEncodedCommentId,
          message: 'i do not like either of you' as UrlEncodedCommentId,
          __draft: true,
          updated: '2015-12-20 15:01:20.396000000' as Timestamp,
          patch_set: 1 as RevisionPatchSetNum,
          path: 'some/path',
        },
      ];

      const actualThreads = createCommentThreads(comments);

      assert.equal(actualThreads.length, 2);

      assert.equal(actualThreads[0].comments.length, 2);
      assert.deepEqual(actualThreads[0].comments[0], comments[0]);
      assert.deepEqual(actualThreads[0].comments[1], comments[1]);
      assert.equal(actualThreads[0].patchNum, 1 as RevisionPatchSetNum);
      assert.equal(actualThreads[0].line, 1);

      assert.equal(actualThreads[1].comments.length, 1);
      assert.deepEqual(actualThreads[1].comments[0], comments[2]);
      assert.equal(actualThreads[1].patchNum, 1 as RevisionPatchSetNum);
      assert.equal(actualThreads[1].line, 'FILE');
    });

    test('derives patchNum and range', () => {
      const comments = [
        {
          id: 'betsys_confession' as UrlEncodedCommentId,
          message: 'i like you, jack',
          updated: '2015-12-24 15:00:10.396000000' as Timestamp,
          range: {
            start_line: 1,
            start_character: 1,
            end_line: 1,
            end_character: 2,
          },
          patch_set: 5 as RevisionPatchSetNum,
          path: '/p',
          line: 1,
        },
      ];

      const expectedThreads = [
        {
          commentSide: CommentSide.REVISION,
          path: '/p',
          rootId: 'betsys_confession' as UrlEncodedCommentId,
          mergeParentNum: undefined,
          comments: [
            {
              id: 'betsys_confession' as UrlEncodedCommentId,
              path: '/p',
              message: 'i like you, jack',
              updated: '2015-12-24 15:00:10.396000000' as Timestamp,
              range: {
                start_line: 1,
                start_character: 1,
                end_line: 1,
                end_character: 2,
              },
              patch_set: 5 as RevisionPatchSetNum,
              line: 1,
            },
          ],
          patchNum: 5 as RevisionPatchSetNum,
          range: {
            start_line: 1,
            start_character: 1,
            end_line: 1,
            end_character: 2,
          },
          line: 1,
        },
      ];

      assert.deepEqual(createCommentThreads(comments), expectedThreads);
    });

    test('does not thread unrelated comments at same location', () => {
      const comments = [
        {
          id: 'sallys_confession' as UrlEncodedCommentId,
          message: 'i like you, jack',
          updated: '2015-12-23 15:00:20.396000000' as Timestamp,
          path: '/p',
        },
        {
          id: 'jacks_reply' as UrlEncodedCommentId,
          message: 'i like you, too',
          updated: '2015-12-24 15:01:20.396000000' as Timestamp,
          path: '/p',
        },
      ];
      assert.equal(createCommentThreads(comments).length, 2);
    });
  });

  test('hasUserSuggestion', () => {
    const comment = {
      ...createComment(),
      message: `${USER_SUGGESTION_START_PATTERN}${'test'}${'\n```'}`,
    };
    assert.isTrue(hasUserSuggestion(comment));
  });

  test('getUserSuggestion', () => {
    const suggestion = 'test';
    const comment = {
      ...createComment(),
      message: `${USER_SUGGESTION_START_PATTERN}${suggestion}${'\n```'}`,
    };
    assert.equal(getUserSuggestion(comment), suggestion);
  });

  suite('getContentInCommentRange', () => {
    test('one line', () => {
      const comment = {
        ...createComment(),
        line: 1,
      };
      const content = 'line1\nline2\nline3';
      assert.equal(getContentInCommentRange(content, comment), 'line1');
    });

    test('multi line', () => {
      const comment = {
        ...createComment(),
        line: 3,
        range: {
          start_line: 1,
          start_character: 5,
          end_line: 3,
          end_character: 39,
        },
      };
      const selectedText =
        '   * Examples:\n' +
        '      * Acknowledge/Dismiss, Delete, Report a bug, Report as not useful,\n' +
        '      * Make blocking, Downgrade severity.';
      const content = `${selectedText}\n`;
      assert.equal(getContentInCommentRange(content, comment), selectedText);
    });
  });

  suite('createUserFixSuggestion', () => {
    test('one line', () => {
      const comment = {
        ...createComment(),
        line: 1,
        path: 'abc.txt',
      };
      const line = 'lane1';
      const replacement = 'line1';
      assert.deepEqual(createUserFixSuggestion(comment, line, replacement), [
        {
          fix_id: PROVIDED_FIX_ID,
          description: 'User suggestion',
          replacements: [
            {
              path: 'abc.txt',
              range: {
                start_line: 1,
                start_character: 0,
                end_line: 1,
                end_character: line.length,
              },
              replacement,
            },
          ],
        },
      ]);
    });

    test('multiline', () => {
      const comment = {
        ...createComment(),
        line: 3,
        range: {
          start_line: 1,
          start_character: 5,
          end_line: 3,
          end_character: 39,
        },
        path: 'abc.txt',
      };
      const line =
        '   * Examples:\n' +
        '      * Acknowledge/Dismiss, Delete, Report a bug, Report as not useful,\n' +
        '      * Make blocking, Downgrade severity.';
      const replacement =
        '   - Examples:\n' +
        '      - Acknowledge/Dismiss, Delete, Report a bug, Report as not useful,\n' +
        '      - Make blocking, Downgrade severity.';
      assert.deepEqual(createUserFixSuggestion(comment, line, replacement), [
        {
          fix_id: PROVIDED_FIX_ID,
          description: 'User suggestion',
          replacements: [
            {
              path: 'abc.txt',
              range: {
                start_line: 1,
                start_character: 0,
                end_line: 3,
                end_character: 42,
              },
              replacement,
            },
          ],
        },
      ]);
    });
  });
});
