/**
 * @license
 * Copyright 2017 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import '../../../test/common-test-setup';
import './gr-admin-view';
import {AdminSubsectionLink, GrAdminView} from './gr-admin-view';
import {getPluginLoader} from '../../shared/gr-js-api-interface/gr-plugin-loader';
import {stubBaseUrl, stubElement, stubRestApi} from '../../../test/test-utils';
import {GerritView} from '../../../services/router/router-model';
import {query, queryAll, queryAndAssert} from '../../../test/test-utils';
import {GrRepoList} from '../gr-repo-list/gr-repo-list';
import {GroupId, GroupName, RepoName, Timestamp} from '../../../types/common';
import {GrDropdownList} from '../../shared/gr-dropdown-list/gr-dropdown-list';
import {GrGroup} from '../gr-group/gr-group';
import {fixture, html, assert} from '@open-wc/testing';
import {AdminChildView} from '../../../models/views/admin';
import {GroupDetailView} from '../../../models/views/group';
import {RepoDetailView} from '../../../models/views/repo';
import {testResolver} from '../../../test/common-test-setup';
import {navigationToken} from '../../core/gr-navigation/gr-navigation';

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

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

  setup(async () => {
    element = await fixture(html`<gr-admin-view></gr-admin-view>`);
    stubRestApi('getProjectConfig').returns(Promise.resolve(undefined));
    const pluginsLoaded = Promise.resolve();
    sinon.stub(getPluginLoader(), 'awaitPluginsLoaded').returns(pluginsLoaded);
    await pluginsLoaded;
    await element.updateComplete;
  });

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

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

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

    element.view = GerritView.ADMIN;
    element.adminViewState = {
      view: GerritView.ADMIN,
      adminView: AdminChildView.REPOS,
    };

    await element.updateComplete;
    assert.equal(queryAll<HTMLLIElement>(element, '.selected').length, 1);
    assert.ok(queryAndAssert<GrRepoList>(element, 'gr-repo-list'));
    assert.isNotOk(query(element, 'gr-admin-create-repo'));
  });

  test('filteredLinks admin', async () => {
    stubRestApi('getAccount').returns(
      Promise.resolve({
        name: 'test-user',
        registered_on: '2015-03-12 18:32:08.000000000' as Timestamp,
      })
    );
    stubRestApi('getAccountCapabilities').returns(
      Promise.resolve(createAdminCapabilities())
    );
    await element.reload();
    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);
  });

  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([
      {capability: null, text: 'internal link text', url: '/internal/link/url'},
      {
        capability: null,
        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: undefined,
        viewableToAll: true,
        target: null,
      });
      assert.deepEqual(element.filteredLinks![2], {
        capability: undefined,
        url: 'http://external/link/url',
        name: 'external link text',
        noBaseUrl: false,
        view: undefined,
        viewableToAll: true,
        target: '_blank',
      });
    });
  });

  test('Repo shows up in nav', async () => {
    element.view = GerritView.REPO;
    element.repoName = 'Test Repo' as RepoName;
    stubRestApi('getAccount').returns(
      Promise.resolve({
        name: 'test-user',
        registered_on: '2015-03-12 18:32:08.000000000' as Timestamp,
      })
    );
    stubRestApi('getAccountCapabilities').returns(
      Promise.resolve(createAdminCapabilities())
    );
    await element.reload();
    await element.updateComplete;
    assert.equal(queryAll<HTMLLIElement>(element, '.sectionTitle').length, 3);
    assert.equal(
      queryAndAssert<HTMLSpanElement>(element, '.breadcrumbText').innerText,
      'Test Repo'
    );
    assert.equal(
      queryAndAssert<GrDropdownList>(element, '#pageSelect').items!.length,
      7
    );
  });

  test('Group shows up in nav', async () => {
    element.groupId = 'a15262' as GroupId;
    element.groupName = 'my-group' as GroupName;
    element.groupIsInternal = true;
    stubRestApi('getIsAdmin').returns(Promise.resolve(true));
    stubRestApi('getAccount').returns(
      Promise.resolve({
        name: 'test-user',
        registered_on: '2015-03-12 18:32:08.000000000' as Timestamp,
      })
    );
    stubRestApi('getAccountCapabilities').returns(
      Promise.resolve(createAdminCapabilities())
    );
    await element.reload();
    await element.updateComplete;
    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('Needs reload when repo changes', async () => {
    stubRestApi('getAccountCapabilities').returns(
      Promise.resolve(createAdminCapabilities())
    );
    stubRestApi('getAccount').returns(
      Promise.resolve({
        _id: 1,
        registered_on: '2015-03-12 18:32:08.000000000' as Timestamp,
      })
    );

    element.view = GerritView.REPO;
    element.repoViewState = {repo: 'Repo 1' as RepoName, view: GerritView.REPO};
    assert.isTrue(element.needsReload());
    await element.reload();
    await element.updateComplete;

    element.repoViewState = {repo: 'Repo 2' as RepoName, view: GerritView.REPO};
    assert.isTrue(element.needsReload());
    await element.updateComplete;
  });

  test('Needs reload when group changes', async () => {
    sinon.stub(element, 'computeGroupName');
    stubRestApi('getAccountCapabilities').returns(
      Promise.resolve(createAdminCapabilities())
    );
    stubRestApi('getAccount').returns(
      Promise.resolve({
        _id: 1,
        registered_on: '2015-03-12 18:32:08.000000000' as Timestamp,
      })
    );
    element.view = GerritView.GROUP;
    element.groupViewState = {groupId: '1' as GroupId, view: GerritView.GROUP};
    assert.isTrue(element.needsReload());
  });

  test('Needs reload when changing from repo to group', async () => {
    element.repoName = 'Test Repo' as RepoName;
    stubRestApi('getAccount').returns(
      Promise.resolve({
        name: 'test-user',
        registered_on: '2015-03-12 18:32:08.000000000' as Timestamp,
      })
    );
    stubRestApi('getAccountCapabilities').returns(
      Promise.resolve(createAdminCapabilities())
    );
    await element.reload();
    await element.updateComplete;

    sinon.stub(element, 'computeGroupName');
    const groupId = '1' as GroupId;
    element.view = GerritView.GROUP;
    element.groupViewState = {groupId, view: GerritView.GROUP};
    await element.updateComplete;

    assert.isTrue(element.needsReload());
    assert.equal(element.groupId, groupId);
  });

  test('Needs reload when group name changes', async () => {
    const newName = 'newName' as GroupName;
    sinon.stub(element, 'computeGroupName');
    element.view = GerritView.GROUP;
    element.groupViewState = {groupId: '1' as GroupId, view: GerritView.GROUP};
    element.groupName = 'oldName' as GroupName;
    assert.isTrue(element.needsReload());
    await element.reload();
    await element.updateComplete;

    queryAndAssert<GrGroup>(element, 'gr-group').dispatchEvent(
      new CustomEvent('name-changed', {
        detail: {name: newName},
        composed: true,
        bubbles: true,
      })
    );
    assert.equal(element.groupName, newName);
  });

  test('dropdown displays if there is a subsection', async () => {
    element.view = GerritView.REPO;
    assert.isNotOk(query(element, '.mainHeader'));
    element.subsectionLinks = [
      {
        text: 'Home',
        value: 'repo',
        view: GerritView.REPO,
        parent: 'my-repo' as RepoName,
        detailType: undefined,
      },
    ];
    await element.updateComplete;
    assert.isOk(query(element, '.mainHeader'));
    element.subsectionLinks = undefined;
    await element.updateComplete;
    assert.isNotOk(query(element, '.mainHeader'));
  });

  test('Dropdown only triggers navigation on explicit select', async () => {
    element.repoName = 'my-repo' as RepoName;
    element.view = GerritView.REPO;
    element.repoViewState = {
      repo: 'my-repo' as RepoName,
      view: GerritView.REPO,
      detail: RepoDetailView.ACCESS,
    };
    stubRestApi('getAccountCapabilities').returns(
      Promise.resolve(createAdminCapabilities())
    );
    stubRestApi('getAccount').returns(
      Promise.resolve({
        _id: 1,
        registered_on: '2015-03-12 18:32:08.000000000' as Timestamp,
      })
    );
    await element.updateComplete;

    const expectedFilteredLinks = [
      {
        name: 'Repositories',
        noBaseUrl: true,
        url: '/admin/repos',
        view: 'gr-repo-list' as GerritView,
        viewableToAll: true,
        subsection: {
          name: 'my-repo',
          view: GerritView.REPO,
          children: [
            {
              name: 'General',
              view: GerritView.REPO,
              url: '/admin/repos/my-repo,general',
              detailType: RepoDetailView.GENERAL,
            },
            {
              name: 'Access',
              view: GerritView.REPO,
              detailType: RepoDetailView.ACCESS,
              url: '/admin/repos/my-repo,access',
            },
            {
              name: 'Commands',
              view: GerritView.REPO,
              detailType: RepoDetailView.COMMANDS,
              url: '/admin/repos/my-repo,commands',
            },
            {
              name: 'Branches',
              view: GerritView.REPO,
              detailType: RepoDetailView.BRANCHES,
              url: '/admin/repos/my-repo,branches',
            },
            {
              name: 'Tags',
              view: GerritView.REPO,
              detailType: RepoDetailView.TAGS,
              url: '/admin/repos/my-repo,tags',
            },
            {
              name: 'Dashboards',
              view: GerritView.REPO,
              detailType: RepoDetailView.DASHBOARDS,
              url: '/admin/repos/my-repo,dashboards',
            },
          ],
        },
      },
      {
        name: 'Groups',
        section: 'Groups',
        noBaseUrl: true,
        url: '/admin/groups',
        view: 'gr-admin-group-list' as GerritView,
      },
      {
        name: 'Plugins',
        capability: 'viewPlugins',
        section: 'Plugins',
        noBaseUrl: true,
        url: '/admin/plugins',
        view: 'gr-plugin-list' as GerritView,
      },
    ];
    const expectedSubsectionLinks = [
      {
        text: 'Home',
        value: 'repo',
        view: GerritView.REPO,
        url: undefined,
        parent: 'my-repo' as RepoName,
        detailType: undefined,
      },
      {
        text: 'General',
        value: 'repogeneral',
        view: GerritView.REPO,
        url: '/admin/repos/my-repo,general',
        detailType: RepoDetailView.GENERAL,
        parent: 'my-repo' as RepoName,
      },
      {
        text: 'Access',
        value: 'repoaccess',
        view: GerritView.REPO,
        url: '/admin/repos/my-repo,access',
        detailType: RepoDetailView.ACCESS,
        parent: 'my-repo' as RepoName,
      },
      {
        text: 'Commands',
        value: 'repocommands',
        view: GerritView.REPO,
        url: '/admin/repos/my-repo,commands',
        detailType: RepoDetailView.COMMANDS,
        parent: 'my-repo' as RepoName,
      },
      {
        text: 'Branches',
        value: 'repobranches',
        view: GerritView.REPO,
        url: '/admin/repos/my-repo,branches',
        detailType: RepoDetailView.BRANCHES,
        parent: 'my-repo' as RepoName,
      },
      {
        text: 'Tags',
        value: 'repotags',
        view: GerritView.REPO,
        url: '/admin/repos/my-repo,tags',
        detailType: RepoDetailView.TAGS,
        parent: 'my-repo' as RepoName,
      },
      {
        text: 'Dashboards',
        value: 'repodashboards',
        view: GerritView.REPO,
        url: '/admin/repos/my-repo,dashboards',
        detailType: RepoDetailView.DASHBOARDS,
        parent: 'my-repo' as RepoName,
      },
    ];
    const setUrlStub = sinon.stub(testResolver(navigationToken), 'setUrl');
    const selectedIsCurrentPageSpy = sinon.spy(
      element,
      'selectedIsCurrentPage'
    );
    sinon.spy(element, 'handleSubsectionChange');
    await element.reload();
    await element.updateComplete;
    assert.deepEqual(element.filteredLinks, expectedFilteredLinks);
    assert.deepEqual(element.subsectionLinks, expectedSubsectionLinks);
    assert.equal(
      queryAndAssert<GrDropdownList>(element, '#pageSelect').value,
      'repoaccess'
    );
    assert.equal(selectedIsCurrentPageSpy.callCount, 1);
    // Doesn't trigger navigation from the page select menu.
    assert.isFalse(setUrlStub.called);

    // When explicitly changed, navigation is called
    queryAndAssert<GrDropdownList>(element, '#pageSelect').value =
      'repogeneral';
    await queryAndAssert<GrDropdownList>(element, '#pageSelect').updateComplete;
    assert.equal(selectedIsCurrentPageSpy.callCount, 2);
    assert.isTrue(setUrlStub.calledOnce);
  });

  test('selectedIsCurrentPage', () => {
    element.repoName = 'my-repo' as RepoName;
    element.view = GerritView.REPO;
    element.repoViewState = {
      view: GerritView.REPO,
      repo: 'my-repo' as RepoName,
    };
    const selected = {
      view: GerritView.REPO,
      parent: 'my-repo' as RepoName,
      value: '',
      text: '',
    } as AdminSubsectionLink;
    assert.isTrue(element.selectedIsCurrentPage(selected));
    selected.parent = 'my-second-repo' as RepoName;
    assert.isFalse(element.selectedIsCurrentPage(selected));
    selected.detailType = RepoDetailView.GENERAL;
    assert.isFalse(element.selectedIsCurrentPage(selected));
  });

  suite('computeSelectedClass', () => {
    setup(async () => {
      stubRestApi('getAccountCapabilities').returns(
        Promise.resolve(createAdminCapabilities())
      );
      stubRestApi('getAccount').returns(
        Promise.resolve({
          _id: 1,
          registered_on: '2015-03-12 18:32:08.000000000' as Timestamp,
        })
      );
      await element.reload();
    });

    test('render', async () => {
      element.view = GerritView.ADMIN;
      element.adminViewState = {
        view: GerritView.ADMIN,
        adminView: AdminChildView.REPOS,
        openCreateModal: false,
      };
      await element.updateComplete;
      assert.shadowDom.equal(
        element,
        /* HTML */ `
          <gr-page-nav class="navStyles">
            <ul class="sectionContent">
              <li class="sectionTitle selected">
                <a
                  class="title"
                  href="//localhost:9876/admin/repos"
                  rel="noopener"
                >
                  Repositories
                </a>
              </li>
              <li class="sectionTitle">
                <a
                  class="title"
                  href="//localhost:9876/admin/groups"
                  rel="noopener"
                >
                  Groups
                </a>
              </li>
              <li class="sectionTitle">
                <a
                  class="title"
                  href="//localhost:9876/admin/plugins"
                  rel="noopener"
                >
                  Plugins
                </a>
              </li>
            </ul>
          </gr-page-nav>
          <div class="main table">
            <gr-repo-list class="table"></gr-repo-list>
          </div>
        `
      );
    });

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

      test('repo list', async () => {
        element.view = GerritView.ADMIN;
        element.adminViewState = {
          view: GerritView.ADMIN,
          adminView: AdminChildView.REPOS,
          openCreateModal: false,
        };
        await element.updateComplete;
        const selected = queryAndAssert(element, 'gr-page-nav .selected');
        assert.isOk(selected);
        assert.equal(selected.textContent!.trim(), 'Repositories');
      });

      test('repo', async () => {
        element.view = GerritView.REPO;
        element.repoViewState = {
          view: GerritView.REPO,
          repo: 'foo' as RepoName,
        };
        element.repoName = 'foo' as RepoName;
        await element.reload();
        await element.updateComplete;
        const selected = queryAndAssert(element, 'gr-page-nav .selected');
        assert.isOk(selected);
        assert.equal(selected.textContent!.trim(), 'foo');
      });

      test('repo access', async () => {
        element.view = GerritView.REPO;
        element.repoViewState = {
          view: GerritView.REPO,
          detail: RepoDetailView.ACCESS,
          repo: 'foo' as RepoName,
        };
        element.repoName = 'foo' as RepoName;
        await element.reload();
        await element.updateComplete;
        const selected = queryAndAssert(element, 'gr-page-nav .selected');
        assert.isOk(selected);
        assert.equal(selected.textContent!.trim(), 'Access');
      });

      test('repo dashboards', async () => {
        element.view = GerritView.REPO;
        element.repoViewState = {
          view: GerritView.REPO,
          detail: RepoDetailView.DASHBOARDS,
          repo: 'foo' as RepoName,
        };
        element.repoName = 'foo' as RepoName;
        await element.reload();
        await element.updateComplete;
        const selected = queryAndAssert(element, 'gr-page-nav .selected');
        assert.isOk(selected);
        assert.equal(selected.textContent!.trim(), 'Dashboards');
      });
    });

    suite('groups', () => {
      let getGroupConfigStub: sinon.SinonStub;

      setup(async () => {
        stubElement('gr-group', 'loadGroup').callsFake(() => Promise.resolve());
        stubElement('gr-group-members', 'loadGroupDetails').callsFake(() =>
          Promise.resolve()
        );

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

      test('group list', async () => {
        element.view = GerritView.ADMIN;
        element.adminViewState = {
          view: GerritView.ADMIN,
          adminView: AdminChildView.GROUPS,
          openCreateModal: false,
        };
        await element.updateComplete;
        const selected = queryAndAssert(element, 'gr-page-nav .selected');
        assert.isOk(selected);
        assert.equal(selected.textContent!.trim(), 'Groups');
      });

      test('internal group', async () => {
        element.view = GerritView.GROUP;
        element.groupViewState = {
          view: GerritView.GROUP,
          groupId: '1234' as GroupId,
        };
        element.groupName = 'foo' as GroupName;
        if (element.needsReload()) await element.reload();
        await element.updateComplete;
        const subsectionItems = queryAll<HTMLLIElement>(
          element,
          '.subsectionItem'
        );
        assert.equal(subsectionItems.length, 2);
        assert.isTrue(element.groupIsInternal);
        const selected = queryAndAssert(element, 'gr-page-nav .selected');
        assert.isOk(selected);
        assert.equal(selected.textContent!.trim(), 'foo');
      });

      test('external group', async () => {
        getGroupConfigStub.returns(
          Promise.resolve({
            name: 'foo',
            id: 'external-id',
          })
        );
        element.view = GerritView.GROUP;
        element.groupViewState = {
          view: GerritView.GROUP,
          groupId: '1234' as GroupId,
        };
        element.groupName = 'foo' as GroupName;
        if (element.needsReload()) await element.reload();
        await element.updateComplete;
        const subsectionItems = queryAll<HTMLLIElement>(
          element,
          '.subsectionItem'
        );
        assert.equal(subsectionItems.length, 0);
        assert.isFalse(element.groupIsInternal);
        const selected = queryAndAssert(element, 'gr-page-nav .selected');
        assert.isOk(selected);
        assert.equal(selected.textContent!.trim(), 'foo');
      });

      test('group members', async () => {
        element.view = GerritView.GROUP;
        element.groupViewState = {
          view: GerritView.GROUP,
          detail: GroupDetailView.MEMBERS,
          groupId: '1234' as GroupId,
        };
        element.groupName = 'foo' as GroupName;
        if (element.needsReload()) await element.reload();
        await element.updateComplete;
        const selected = queryAndAssert(element, 'gr-page-nav .selected');
        assert.isOk(selected);
        assert.equal(selected.textContent!.trim(), 'Members');
      });
    });
  });
});
