/**
 * @license
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import '../../../test/common-test-setup-karma.js';
import './gr-linked-text.js';
import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
import {html} from '@polymer/polymer/lib/utils/html-tag.js';

const basicFixture = fixtureFromTemplate(html`
<gr-linked-text>
      <div id="output"></div>
    </gr-linked-text>
`);

suite('gr-linked-text tests', () => {
  let element;

  let originalCanonicalPath;

  setup(() => {
    originalCanonicalPath = window.CANONICAL_PATH;
    element = basicFixture.instantiate();

    sinon.stub(GerritNav, 'mapCommentlinks').value( x => x);
    element.config = {
      ph: {
        match: '([Bb]ug|[Ii]ssue)\\s*#?(\\d+)',
        link: 'https://bugs.chromium.org/p/gerrit/issues/detail?id=$2',
      },
      prefixsameinlinkandpattern: {
        match: '([Hh][Tt][Tt][Pp]example)\\s*#?(\\d+)',
        link: 'https://bugs.chromium.org/p/gerrit/issues/detail?id=$2',
      },
      changeid: {
        match: '(I[0-9a-f]{8,40})',
        link: '#/q/$1',
      },
      changeid2: {
        match: 'Change-Id: +(I[0-9a-f]{8,40})',
        link: '#/q/$1',
      },
      googlesearch: {
        match: 'google:(.+)',
        link: 'https://bing.com/search?q=$1', // html should supercede link.
        html: '<a href="https://google.com/search?q=$1">$1</a>',
      },
      hashedhtml: {
        match: 'hash:(.+)',
        html: '<a href="#/awesomesauce">$1</a>',
      },
      baseurl: {
        match: 'test (.+)',
        html: '<a href="/r/awesomesauce">$1</a>',
      },
      anotatstartwithbaseurl: {
        match: 'a test (.+)',
        html: '[Lookup: <a href="/r/awesomesauce">$1</a>]',
      },
      disabledconfig: {
        match: 'foo:(.+)',
        link: 'https://google.com/search?q=$1',
        enabled: false,
      },
    };
  });

  teardown(() => {
    window.CANONICAL_PATH = originalCanonicalPath;
  });

  test('URL pattern was parsed and linked.', () => {
    // Regular inline link.
    const url = 'https://bugs.chromium.org/p/gerrit/issues/detail?id=3650';
    element.content = url;
    const linkEl = element.$.output.childNodes[0];
    assert.equal(linkEl.target, '_blank');
    assert.equal(linkEl.rel, 'noopener');
    assert.equal(linkEl.href, url);
    assert.equal(linkEl.textContent, url);
  });

  test('Bug pattern was parsed and linked', () => {
    // "Issue/Bug" pattern.
    element.content = 'Issue 3650';

    let linkEl = element.$.output.childNodes[0];
    const url = 'https://bugs.chromium.org/p/gerrit/issues/detail?id=3650';
    assert.equal(linkEl.target, '_blank');
    assert.equal(linkEl.href, url);
    assert.equal(linkEl.textContent, 'Issue 3650');

    element.content = 'Bug 3650';
    linkEl = element.$.output.childNodes[0];
    assert.equal(linkEl.target, '_blank');
    assert.equal(linkEl.rel, 'noopener');
    assert.equal(linkEl.href, url);
    assert.equal(linkEl.textContent, 'Bug 3650');
  });

  test('Pattern with same prefix as link was correctly parsed', () => {
    // Pattern starts with the same prefix (`http`) as the url.
    element.content = 'httpexample 3650';

    assert.equal(element.$.output.childNodes.length, 1);
    const linkEl = element.$.output.childNodes[0];
    const url = 'https://bugs.chromium.org/p/gerrit/issues/detail?id=3650';
    assert.equal(linkEl.target, '_blank');
    assert.equal(linkEl.href, url);
    assert.equal(linkEl.textContent, 'httpexample 3650');
  });

  test('Change-Id pattern was parsed and linked', () => {
    // "Change-Id:" pattern.
    const changeID = 'I11d6a37f5e9b5df0486f6c922d8836dfa780e03e';
    const prefix = 'Change-Id: ';
    element.content = prefix + changeID;

    const textNode = element.$.output.childNodes[0];
    const linkEl = element.$.output.childNodes[1];
    assert.equal(textNode.textContent, prefix);
    const url = '/q/' + changeID;
    assert.isFalse(linkEl.hasAttribute('target'));
    // Since url is a path, the host is added automatically.
    assert.isTrue(linkEl.href.endsWith(url));
    assert.equal(linkEl.textContent, changeID);
  });

  test('Change-Id pattern was parsed and linked with base url', () => {
    window.CANONICAL_PATH = '/r';

    // "Change-Id:" pattern.
    const changeID = 'I11d6a37f5e9b5df0486f6c922d8836dfa780e03e';
    const prefix = 'Change-Id: ';
    element.content = prefix + changeID;

    const textNode = element.$.output.childNodes[0];
    const linkEl = element.$.output.childNodes[1];
    assert.equal(textNode.textContent, prefix);
    const url = '/r/q/' + changeID;
    assert.isFalse(linkEl.hasAttribute('target'));
    // Since url is a path, the host is added automatically.
    assert.isTrue(linkEl.href.endsWith(url));
    assert.equal(linkEl.textContent, changeID);
  });

  test('Multiple matches', () => {
    element.content = 'Issue 3650\nIssue 3450';
    const linkEl1 = element.$.output.childNodes[0];
    const linkEl2 = element.$.output.childNodes[2];

    assert.equal(linkEl1.target, '_blank');
    assert.equal(linkEl1.href,
        'https://bugs.chromium.org/p/gerrit/issues/detail?id=3650');
    assert.equal(linkEl1.textContent, 'Issue 3650');

    assert.equal(linkEl2.target, '_blank');
    assert.equal(linkEl2.href,
        'https://bugs.chromium.org/p/gerrit/issues/detail?id=3450');
    assert.equal(linkEl2.textContent, 'Issue 3450');
  });

  test('Change-Id pattern parsed before bug pattern', () => {
    // "Change-Id:" pattern.
    const changeID = 'I11d6a37f5e9b5df0486f6c922d8836dfa780e03e';
    const prefix = 'Change-Id: ';

    // "Issue/Bug" pattern.
    const bug = 'Issue 3650';

    const changeUrl = '/q/' + changeID;
    const bugUrl = 'https://bugs.chromium.org/p/gerrit/issues/detail?id=3650';

    element.content = prefix + changeID + bug;

    const textNode = element.$.output.childNodes[0];
    const changeLinkEl = element.$.output.childNodes[1];
    const bugLinkEl = element.$.output.childNodes[2];

    assert.equal(textNode.textContent, prefix);

    assert.isFalse(changeLinkEl.hasAttribute('target'));
    assert.isTrue(changeLinkEl.href.endsWith(changeUrl));
    assert.equal(changeLinkEl.textContent, changeID);

    assert.equal(bugLinkEl.target, '_blank');
    assert.equal(bugLinkEl.href, bugUrl);
    assert.equal(bugLinkEl.textContent, 'Issue 3650');
  });

  test('html field in link config', () => {
    element.content = 'google:do a barrel roll';
    const linkEl = element.$.output.childNodes[0];
    assert.equal(linkEl.getAttribute('href'),
        'https://google.com/search?q=do a barrel roll');
    assert.equal(linkEl.textContent, 'do a barrel roll');
  });

  test('removing hash from links', () => {
    element.content = 'hash:foo';
    const linkEl = element.$.output.childNodes[0];
    assert.isTrue(linkEl.href.endsWith('/awesomesauce'));
    assert.equal(linkEl.textContent, 'foo');
  });

  test('html with base url', () => {
    window.CANONICAL_PATH = '/r';

    element.content = 'test foo';
    const linkEl = element.$.output.childNodes[0];
    assert.isTrue(linkEl.href.endsWith('/r/awesomesauce'));
    assert.equal(linkEl.textContent, 'foo');
  });

  test('a is not at start', () => {
    window.CANONICAL_PATH = '/r';

    element.content = 'a test foo';
    const linkEl = element.$.output.childNodes[1];
    assert.isTrue(linkEl.href.endsWith('/r/awesomesauce'));
    assert.equal(linkEl.textContent, 'foo');
  });

  test('hash html with base url', () => {
    window.CANONICAL_PATH = '/r';

    element.content = 'hash:foo';
    const linkEl = element.$.output.childNodes[0];
    assert.isTrue(linkEl.href.endsWith('/r/awesomesauce'));
    assert.equal(linkEl.textContent, 'foo');
  });

  test('disabled config', () => {
    element.content = 'foo:baz';
    assert.equal(element.$.output.innerHTML, 'foo:baz');
  });

  test('R=email labels link correctly', () => {
    element.removeZeroWidthSpace = true;
    element.content = 'R=\u200Btest@google.com';
    assert.equal(element.$.output.textContent, 'R=test@google.com');
    assert.equal(element.$.output.innerHTML.match(/(R=<a)/g).length, 1);
  });

  test('CC=email labels link correctly', () => {
    element.removeZeroWidthSpace = true;
    element.content = 'CC=\u200Btest@google.com';
    assert.equal(element.$.output.textContent, 'CC=test@google.com');
    assert.equal(element.$.output.innerHTML.match(/(CC=<a)/g).length, 1);
  });

  test('only {http,https,mailto} protocols are linkified', () => {
    element.content = 'xx mailto:test@google.com yy';
    let links = element.$.output.querySelectorAll('a');
    assert.equal(links.length, 1);
    assert.equal(links[0].getAttribute('href'), 'mailto:test@google.com');
    assert.equal(links[0].innerHTML, 'mailto:test@google.com');

    element.content = 'xx http://google.com yy';
    links = element.$.output.querySelectorAll('a');
    assert.equal(links.length, 1);
    assert.equal(links[0].getAttribute('href'), 'http://google.com');
    assert.equal(links[0].innerHTML, 'http://google.com');

    element.content = 'xx https://google.com yy';
    links = element.$.output.querySelectorAll('a');
    assert.equal(links.length, 1);
    assert.equal(links[0].getAttribute('href'), 'https://google.com');
    assert.equal(links[0].innerHTML, 'https://google.com');

    element.content = 'xx ssh://google.com yy';
    links = element.$.output.querySelectorAll('a');
    assert.equal(links.length, 0);

    element.content = 'xx ftp://google.com yy';
    links = element.$.output.querySelectorAll('a');
    assert.equal(links.length, 0);
  });

  test('links without leading whitespace are linkified', () => {
    element.content = 'xx abcmailto:test@google.com yy';
    assert.equal(element.$.output.innerHTML.substr(0, 6), 'xx abc');
    let links = element.$.output.querySelectorAll('a');
    assert.equal(links.length, 1);
    assert.equal(links[0].getAttribute('href'), 'mailto:test@google.com');
    assert.equal(links[0].innerHTML, 'mailto:test@google.com');

    element.content = 'xx defhttp://google.com yy';
    assert.equal(element.$.output.innerHTML.substr(0, 6), 'xx def');
    links = element.$.output.querySelectorAll('a');
    assert.equal(links.length, 1);
    assert.equal(links[0].getAttribute('href'), 'http://google.com');
    assert.equal(links[0].innerHTML, 'http://google.com');

    element.content = 'xx qwehttps://google.com yy';
    assert.equal(element.$.output.innerHTML.substr(0, 6), 'xx qwe');
    links = element.$.output.querySelectorAll('a');
    assert.equal(links.length, 1);
    assert.equal(links[0].getAttribute('href'), 'https://google.com');
    assert.equal(links[0].innerHTML, 'https://google.com');

    // Non-latin character
    element.content = 'xx абвhttps://google.com yy';
    assert.equal(element.$.output.innerHTML.substr(0, 6), 'xx абв');
    links = element.$.output.querySelectorAll('a');
    assert.equal(links.length, 1);
    assert.equal(links[0].getAttribute('href'), 'https://google.com');
    assert.equal(links[0].innerHTML, 'https://google.com');

    element.content = 'xx ssh://google.com yy';
    links = element.$.output.querySelectorAll('a');
    assert.equal(links.length, 0);

    element.content = 'xx ftp://google.com yy';
    links = element.$.output.querySelectorAll('a');
    assert.equal(links.length, 0);
  });

  test('overlapping links', () => {
    element.config = {
      b1: {
        match: '(B:\\s*)(\\d+)',
        html: '$1<a href="ftp://foo/$2">$2</a>',
      },
      b2: {
        match: '(B:\\s*\\d+\\s*,\\s*)(\\d+)',
        html: '$1<a href="ftp://foo/$2">$2</a>',
      },
    };
    element.content = '- B: 123, 45';
    const links = element.root.querySelectorAll('a');

    assert.equal(links.length, 2);
    assert.equal(element.shadowRoot
        .querySelector('span').textContent, '- B: 123, 45');

    assert.equal(links[0].href, 'ftp://foo/123');
    assert.equal(links[0].textContent, '123');

    assert.equal(links[1].href, 'ftp://foo/45');
    assert.equal(links[1].textContent, '45');
  });

  test('_contentOrConfigChanged called with config', () => {
    const contentStub = sinon.stub(element, '_contentChanged');
    const contentConfigStub = sinon.stub(element, '_contentOrConfigChanged');
    element.content = 'some text';
    assert.isTrue(contentStub.called);
    assert.isTrue(contentConfigStub.called);
  });
});

