/**
 * @license
 * Copyright (C) 2017 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-admin-view.js';
import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
import {getPluginLoader} from '../../shared/gr-js-api-interface/gr-plugin-loader.js';
import {stubBaseUrl, stubRestApi} from '../../../test/test-utils.js';
import {GerritView} from '../../../services/router/router-model.js';

const basicFixture = fixtureFromElement('gr-admin-view');

function createAdminCapabilities() {
  return {
    createGroup: true,
    createProject: true,
    viewPlugins: true,
  };
}

suite('gr-admin-view tests', () => {
  let element;

  setup(done => {
    element = basicFixture.instantiate();
    stubRestApi('getProjectConfig').returns(Promise.resolve({}));
    const pluginsLoaded = Promise.resolve();
    sinon.stub(getPluginLoader(), 'awaitPluginsLoaded').returns(pluginsLoaded);
    pluginsLoaded.then(() => flush(done));
  });

  test('_computeURLHelper', () => {
    const path = '/test';
    const host = 'http://www.testsite.com';
    const computedPath = element._computeURLHelper(host, path);
    assert.equal(computedPath, '//http://www.testsite.com/test');
  });

  test('link URLs', () => {
    assert.equal(
        element._computeLinkURL({url: '/test', noBaseUrl: true}),
        '//' + window.location.host + '/test');

    stubBaseUrl('/foo');
    assert.equal(
        element._computeLinkURL({url: '/test', noBaseUrl: true}),
        '//' + window.location.host + '/foo/test');
    assert.equal(element._computeLinkURL({url: '/test'}), '/test');
    assert.equal(
        element._computeLinkURL({url: '/test', target: '_blank'}),
        '/test');
  });

  test('current page gets selected and is displayed', () => {
    element._filteredLinks = [{
      name: 'Repositories',
      url: '/admin/repos',
      view: 'gr-repo-list',
    }];

    element.params = {
      view: 'admin',
      adminView: 'gr-repo-list',
    };

    flush();
    assert.equal(element.root.querySelectorAll(
        '.selected').length, 1);
    assert.ok(element.shadowRoot
        .querySelector('gr-repo-list'));
    assert.isNotOk(element.shadowRoot
        .querySelector('gr-admin-create-repo'));
  });

  test('_filteredLinks admin', done => {
    stubRestApi('getAccount').returns(Promise.resolve({
      name: 'test-user',
    }));
    stubRestApi('getAccountCapabilities').returns(
        Promise.resolve(createAdminCapabilities()));
    element.reload().then(() => {
      assert.equal(element._filteredLinks.length, 3);

      // Repos
      assert.isNotOk(element._filteredLinks[0].subsection);

      // Groups
      assert.isNotOk(element._filteredLinks[0].subsection);

      // Plugins
      assert.isNotOk(element._filteredLinks[0].subsection);
      done();
    });
  });

  test('_filteredLinks non admin authenticated', async () => {
    await element.reload();
    assert.equal(element._filteredLinks.length, 2);
    // Repos
    assert.isNotOk(element._filteredLinks[0].subsection);
    // Groups
    assert.isNotOk(element._filteredLinks[0].subsection);
  });

  test('_filteredLinks non admin unathenticated', async () => {
    stubRestApi('getAccount').returns(Promise.resolve(undefined));
    await element.reload();
    assert.equal(element._filteredLinks.length, 1);
    // Repos
    assert.isNotOk(element._filteredLinks[0].subsection);
  });

  test('_filteredLinks from plugin', () => {
    stubRestApi('getAccount').returns(Promise.resolve(undefined));
    sinon.stub(element.jsAPI, 'getAdminMenuLinks').returns([
      {text: 'internal link text', url: '/internal/link/url'},
      {text: 'external link text', url: 'http://external/link/url'},
    ]);
    return element.reload().then(() => {
      assert.equal(element._filteredLinks.length, 3);
      assert.deepEqual(element._filteredLinks[1], {
        capability: undefined,
        url: '/internal/link/url',
        name: 'internal link text',
        noBaseUrl: true,
        view: null,
        viewableToAll: true,
        target: null,
      });
      assert.deepEqual(element._filteredLinks[2], {
        capability: undefined,
        url: 'http://external/link/url',
        name: 'external link text',
        noBaseUrl: false,
        view: null,
        viewableToAll: true,
        target: '_blank',
      });
    });
  });

  test('Repo shows up in nav', done => {
    element._repoName = 'Test Repo';
    stubRestApi('getAccount').returns(Promise.resolve({
      name: 'test-user',
    }));
    stubRestApi('getAccountCapabilities').returns(
        Promise.resolve(createAdminCapabilities()));
    element.reload().then(() => {
      flush();
      assert.equal(dom(element.root)
          .querySelectorAll('.sectionTitle').length, 3);
      assert.equal(element.shadowRoot
          .querySelector('.breadcrumbText').innerText, 'Test Repo');
      assert.equal(
          element.shadowRoot.querySelector('#pageSelect').items.length,
          6
      );
      done();
    });
  });

  test('Group shows up in nav', async () => {
    element._groupId = 'a15262';
    element._groupName = 'my-group';
    element._groupIsInternal = true;
    element._isAdmin = true;
    element._groupOwner = false;
    stubRestApi('getAccount').returns(Promise.resolve({name: 'test-user'}));
    stubRestApi('getAccountCapabilities').returns(
        Promise.resolve(createAdminCapabilities()));
    await element.reload();
    await flush();
    assert.equal(element._filteredLinks.length, 3);
    // Repos
    assert.isNotOk(element._filteredLinks[0].subsection);
    // Groups
    assert.equal(element._filteredLinks[1].subsection.children.length, 2);
    assert.equal(element._filteredLinks[1].subsection.name, 'my-group');
    // Plugins
    assert.isNotOk(element._filteredLinks[2].subsection);
  });

  test('Nav is reloaded when repo changes', () => {
    stubRestApi('getAccountCapabilities').returns(
        Promise.resolve(createAdminCapabilities()));
    stubRestApi('getAccount').returns(Promise.resolve({_id: 1}));
    sinon.stub(element, 'reload');
    element.params = {repo: 'Test Repo', view: GerritView.REPO};
    assert.equal(element.reload.callCount, 1);
    element.params = {repo: 'Test Repo 2',
      view: GerritView.REPO};
    assert.equal(element.reload.callCount, 2);
  });

  test('Nav is reloaded when group changes', () => {
    sinon.stub(element, '_computeGroupName');
    stubRestApi('getAccountCapabilities').returns(
        Promise.resolve(createAdminCapabilities()));
    stubRestApi('getAccount').returns(Promise.resolve({_id: 1}));
    sinon.stub(element, 'reload');
    element.params = {groupId: '1', view: GerritView.GROUP};
    assert.equal(element.reload.callCount, 1);
  });

  test('Nav is reloaded when group name changes', done => {
    const newName = 'newName';
    sinon.stub(element, '_computeGroupName');
    sinon.stub(element, 'reload').callsFake(() => {
      assert.equal(element._groupName, newName);
      assert.isTrue(element.reload.called);
      done();
    });
    element.params = {group: 1, view: GerritNav.View.GROUP};
    element._groupName = 'oldName';
    flush();
    element.shadowRoot
        .querySelector('gr-group').dispatchEvent(
            new CustomEvent('name-changed', {
              detail: {name: newName},
              composed: true, bubbles: true,
            }));
  });

  test('dropdown displays if there is a subsection', () => {
    assert.isNotOk(element.shadowRoot
        .querySelector('.mainHeader'));
    element._subsectionLinks = [
      {
        text: 'Home',
        value: 'repo',
        view: 'repo',
        url: '',
        parent: 'my-repo',
        detailType: undefined,
      },
    ];
    flush();
    assert.isOk(element.shadowRoot
        .querySelector('.mainHeader'));
    element._subsectionLinks = undefined;
    flush();
    assert.equal(
        getComputedStyle(element.shadowRoot
            .querySelector('.mainHeader')).display,
        'none');
  });

  test('Dropdown only triggers navigation on explicit select', done => {
    element._repoName = 'my-repo';
    element.params = {
      repo: 'my-repo',
      view: GerritNav.View.REPO,
      detail: GerritNav.RepoDetailView.ACCESS,
    };
    stubRestApi('getAccountCapabilities').returns(
        Promise.resolve(createAdminCapabilities()));
    stubRestApi('getAccount').returns(Promise.resolve({_id: 1}));
    flush();
    const expectedFilteredLinks = [
      {
        name: 'Repositories',
        noBaseUrl: true,
        url: '/admin/repos',
        view: 'gr-repo-list',
        viewableToAll: true,
        subsection: {
          name: 'my-repo',
          view: 'repo',
          url: '',
          children: [
            {
              name: 'Access',
              view: 'repo',
              detailType: 'access',
              url: '',
            },
            {
              name: 'Commands',
              view: 'repo',
              detailType: 'commands',
              url: '',
            },
            {
              name: 'Branches',
              view: 'repo',
              detailType: 'branches',
              url: '',
            },
            {
              name: 'Tags',
              view: 'repo',
              detailType: 'tags',
              url: '',
            },
            {
              name: 'Dashboards',
              view: 'repo',
              detailType: 'dashboards',
              url: '',
            },
          ],
        },
      },
      {
        name: 'Groups',
        section: 'Groups',
        noBaseUrl: true,
        url: '/admin/groups',
        view: 'gr-admin-group-list',
      },
      {
        name: 'Plugins',
        capability: 'viewPlugins',
        section: 'Plugins',
        noBaseUrl: true,
        url: '/admin/plugins',
        view: 'gr-plugin-list',
      },
    ];
    const expectedSubsectionLinks = [
      {
        text: 'Home',
        value: 'repo',
        view: 'repo',
        url: '',
        parent: 'my-repo',
        detailType: undefined,
      },
      {
        text: 'Access',
        value: 'repoaccess',
        view: 'repo',
        url: '',
        detailType: 'access',
        parent: 'my-repo',
      },
      {
        text: 'Commands',
        value: 'repocommands',
        view: 'repo',
        url: '',
        detailType: 'commands',
        parent: 'my-repo',
      },
      {
        text: 'Branches',
        value: 'repobranches',
        view: 'repo',
        url: '',
        detailType: 'branches',
        parent: 'my-repo',
      },
      {
        text: 'Tags',
        value: 'repotags',
        view: 'repo',
        url: '',
        detailType: 'tags',
        parent: 'my-repo',
      },
      {
        text: 'Dashboards',
        value: 'repodashboards',
        view: 'repo',
        url: '',
        detailType: 'dashboards',
        parent: 'my-repo',
      },
    ];
    sinon.stub(GerritNav, 'navigateToRelativeUrl');
    sinon.spy(element, '_selectedIsCurrentPage');
    sinon.spy(element, '_handleSubsectionChange');
    element.reload().then(() => {
      assert.deepEqual(element._filteredLinks, expectedFilteredLinks);
      assert.deepEqual(element._subsectionLinks, expectedSubsectionLinks);
      assert.equal(
          element.shadowRoot.querySelector('#pageSelect').value,
          'repoaccess'
      );
      assert.isTrue(element._selectedIsCurrentPage.calledOnce);
      // Doesn't trigger navigation from the page select menu.
      assert.isFalse(GerritNav.navigateToRelativeUrl.called);

      // When explicitly changed, navigation is called
      element.shadowRoot.querySelector('#pageSelect').value = 'repo';
      assert.isTrue(element._selectedIsCurrentPage.calledTwice);
      assert.isTrue(GerritNav.navigateToRelativeUrl.calledOnce);
      done();
    });
  });

  test('_selectedIsCurrentPage', () => {
    element._repoName = 'my-repo';
    element.params = {view: 'repo', repo: 'my-repo'};
    const selected = {
      view: 'repo',
      detailType: undefined,
      parent: 'my-repo',
    };
    assert.isTrue(element._selectedIsCurrentPage(selected));
    selected.parent = 'my-second-repo';
    assert.isFalse(element._selectedIsCurrentPage(selected));
    selected.detailType = 'detailType';
    assert.isFalse(element._selectedIsCurrentPage(selected));
  });

  suite('_computeSelectedClass', () => {
    setup(() => {
      stubRestApi('getAccountCapabilities').returns(
          Promise.resolve(createAdminCapabilities()));
      stubRestApi('getAccount').returns(Promise.resolve({_id: 1}));
      return element.reload();
    });

    suite('repos', () => {
      setup(() => {
        stub('gr-repo-access', '_repoChanged').callsFake(() => {});
      });

      test('repo list', () => {
        element.params = {
          view: GerritNav.View.ADMIN,
          adminView: 'gr-repo-list',
          openCreateModal: false,
        };
        flush();
        const selected = element.shadowRoot
            .querySelector('gr-page-nav .selected');
        assert.isOk(selected);
        assert.equal(selected.textContent.trim(), 'Repositories');
      });

      test('repo', () => {
        element.params = {
          view: GerritNav.View.REPO,
          repoName: 'foo',
        };
        element._repoName = 'foo';
        return element.reload().then(() => {
          flush();
          const selected = element.shadowRoot
              .querySelector('gr-page-nav .selected');
          assert.isOk(selected);
          assert.equal(selected.textContent.trim(), 'foo');
        });
      });

      test('repo access', () => {
        element.params = {
          view: GerritNav.View.REPO,
          detail: GerritNav.RepoDetailView.ACCESS,
          repoName: 'foo',
        };
        element._repoName = 'foo';
        return element.reload().then(() => {
          flush();
          const selected = element.shadowRoot
              .querySelector('gr-page-nav .selected');
          assert.isOk(selected);
          assert.equal(selected.textContent.trim(), 'Access');
        });
      });

      test('repo dashboards', () => {
        element.params = {
          view: GerritNav.View.REPO,
          detail: GerritNav.RepoDetailView.DASHBOARDS,
          repoName: 'foo',
        };
        element._repoName = 'foo';
        return element.reload().then(() => {
          flush();
          const selected = element.shadowRoot
              .querySelector('gr-page-nav .selected');
          assert.isOk(selected);
          assert.equal(selected.textContent.trim(), 'Dashboards');
        });
      });
    });

    suite('groups', () => {
      let getGroupConfigStub;
      setup(() => {
        stub('gr-group', '_loadGroup').callsFake(() => Promise.resolve({}));
        stub('gr-group-members', '_loadGroupDetails').callsFake(() => {});

        getGroupConfigStub = stubRestApi('getGroupConfig');
        getGroupConfigStub.returns(Promise.resolve({
          name: 'foo',
          id: 'c0f83e941ce90caea30e6ad88f0d4ea0e841a7a9',
        }));
        stubRestApi('getIsGroupOwner')
            .returns(Promise.resolve(true));
        return element.reload();
      });

      test('group list', () => {
        element.params = {
          view: GerritNav.View.ADMIN,
          adminView: 'gr-admin-group-list',
          openCreateModal: false,
        };
        flush();
        const selected = element.shadowRoot
            .querySelector('gr-page-nav .selected');
        assert.isOk(selected);
        assert.equal(selected.textContent.trim(), 'Groups');
      });

      test('internal group', () => {
        element.params = {
          view: GerritNav.View.GROUP,
          groupId: 1234,
        };
        element._groupName = 'foo';
        return element.reload().then(() => {
          flush();
          const subsectionItems = dom(element.root)
              .querySelectorAll('.subsectionItem');
          assert.equal(subsectionItems.length, 2);
          assert.isTrue(element._groupIsInternal);
          const selected = element.shadowRoot
              .querySelector('gr-page-nav .selected');
          assert.isOk(selected);
          assert.equal(selected.textContent.trim(), 'foo');
        });
      });

      test('external group', () => {
        getGroupConfigStub.returns(Promise.resolve({
          name: 'foo',
          id: 'external-id',
        }));
        element.params = {
          view: GerritNav.View.GROUP,
          groupId: 1234,
        };
        element._groupName = 'foo';
        return element.reload().then(() => {
          flush();
          const subsectionItems = dom(element.root)
              .querySelectorAll('.subsectionItem');
          assert.equal(subsectionItems.length, 0);
          assert.isFalse(element._groupIsInternal);
          const selected = element.shadowRoot
              .querySelector('gr-page-nav .selected');
          assert.isOk(selected);
          assert.equal(selected.textContent.trim(), 'foo');
        });
      });

      test('group members', () => {
        element.params = {
          view: GerritNav.View.GROUP,
          detail: GerritNav.GroupDetailView.MEMBERS,
          groupId: 1234,
        };
        element._groupName = 'foo';
        return element.reload().then(() => {
          flush();
          const selected = element.shadowRoot
              .querySelector('gr-page-nav .selected');
          assert.isOk(selected);
          assert.equal(selected.textContent.trim(), 'Members');
        });
      });
    });
  });
});

