/**
 * @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-repo.js';
import {PolymerElement} from '@polymer/polymer/polymer-element.js';

const basicFixture = fixtureFromElement('gr-repo');

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

  let repoStub;
  const repoConf = {
    description: 'Access inherited by all other projects.',
    use_contributor_agreements: {
      value: false,
      configured_value: 'FALSE',
    },
    use_content_merge: {
      value: false,
      configured_value: 'FALSE',
    },
    use_signed_off_by: {
      value: false,
      configured_value: 'FALSE',
    },
    create_new_change_for_all_not_in_target: {
      value: false,
      configured_value: 'FALSE',
    },
    require_change_id: {
      value: false,
      configured_value: 'FALSE',
    },
    enable_signed_push: {
      value: false,
      configured_value: 'FALSE',
    },
    require_signed_push: {
      value: false,
      configured_value: 'FALSE',
    },
    reject_implicit_merges: {
      value: false,
      configured_value: 'FALSE',
    },
    private_by_default: {
      value: false,
      configured_value: 'FALSE',
    },
    match_author_to_committer_date: {
      value: false,
      configured_value: 'FALSE',
    },
    reject_empty_commit: {
      value: false,
      configured_value: 'FALSE',
    },
    enable_reviewer_by_email: {
      value: false,
      configured_value: 'FALSE',
    },
    max_object_size_limit: {},
    submit_type: 'MERGE_IF_NECESSARY',
    default_submit_type: {
      value: 'MERGE_IF_NECESSARY',
      configured_value: 'INHERIT',
      inherited_value: 'MERGE_IF_NECESSARY',
    },
  };

  const REPO = 'test-repo';
  const SCHEMES = {http: {}, repo: {}, ssh: {}};

  function getFormFields() {
    const selects = Array.from(
        element.root.querySelectorAll('select'));
    const textareas = Array.from(
        element.root.querySelectorAll('iron-autogrow-textarea'));
    const inputs = Array.from(
        element.root.querySelectorAll('input'));
    return inputs.concat(textareas).concat(selects);
  }

  setup(() => {
    stub('gr-rest-api-interface', {
      getLoggedIn() { return Promise.resolve(false); },
      getConfig() {
        return Promise.resolve({download: {}});
      },
    });
    element = basicFixture.instantiate();
    repoStub = sinon.stub(
        element.$.restAPI,
        'getProjectConfig')
        .callsFake(() => Promise.resolve(repoConf));
  });

  test('_computePluginData', () => {
    assert.deepEqual(element._computePluginData(), []);
    assert.deepEqual(element._computePluginData({}), []);
    assert.deepEqual(element._computePluginData({base: {}}), []);
    assert.deepEqual(element._computePluginData({base: {plugin: 'data'}}),
        [{name: 'plugin', config: 'data'}]);
  });

  test('_handlePluginConfigChanged', () => {
    const notifyStub = sinon.stub(element, 'notifyPath');
    element._repoConfig = {plugin_config: {}};
    element._handlePluginConfigChanged({detail: {
      name: 'test',
      config: 'data',
      notifyPath: 'path',
    }});
    flush();

    assert.equal(element._repoConfig.plugin_config.test, 'data');
    assert.equal(notifyStub.lastCall.args[0],
        '_repoConfig.plugin_config.path');
  });

  test('loading displays before repo config is loaded', () => {
    assert.isTrue(element.$.loading.classList.contains('loading'));
    assert.isFalse(getComputedStyle(element.$.loading).display === 'none');
    assert.isTrue(element.$.loadedContent.classList.contains('loading'));
    assert.isTrue(getComputedStyle(element.$.loadedContent)
        .display === 'none');
  });

  test('download commands visibility', () => {
    element._loading = false;
    flush();
    assert.isTrue(element.$.downloadContent.classList.contains('hide'));
    assert.isTrue(getComputedStyle(element.$.downloadContent)
        .display == 'none');
    element._schemesObj = SCHEMES;
    flush();
    assert.isFalse(element.$.downloadContent.classList.contains('hide'));
    assert.isFalse(getComputedStyle(element.$.downloadContent)
        .display == 'none');
  });

  test('form defaults to read only', () => {
    assert.isTrue(element._readOnly);
  });

  test('form defaults to read only when not logged in', done => {
    element.repo = REPO;
    element._loadRepo().then(() => {
      assert.isTrue(element._readOnly);
      done();
    });
  });

  test('form defaults to read only when logged in and not admin', done => {
    element.repo = REPO;
    sinon.stub(element, '_getLoggedIn').callsFake(() => Promise.resolve(true));
    sinon.stub(
        element.$.restAPI,
        'getRepoAccess')
        .callsFake(() => Promise.resolve({'test-repo': {}}));
    element._loadRepo().then(() => {
      assert.isTrue(element._readOnly);
      done();
    });
  });

  test('all form elements are disabled when not admin', done => {
    element.repo = REPO;
    element._loadRepo().then(() => {
      flush();
      const formFields = getFormFields();
      for (const field of formFields) {
        assert.isTrue(field.hasAttribute('disabled'));
      }
      done();
    });
  });

  test('_formatBooleanSelect', () => {
    let item = {inherited_value: true};
    assert.deepEqual(element._formatBooleanSelect(item), [
      {
        label: 'Inherit (true)',
        value: 'INHERIT',
      },
      {
        label: 'True',
        value: 'TRUE',
      }, {
        label: 'False',
        value: 'FALSE',
      },
    ]);

    item = {inherited_value: false};
    assert.deepEqual(element._formatBooleanSelect(item), [
      {
        label: 'Inherit (false)',
        value: 'INHERIT',
      },
      {
        label: 'True',
        value: 'TRUE',
      }, {
        label: 'False',
        value: 'FALSE',
      },
    ]);

    // For items without inherited values
    item = {};
    assert.deepEqual(element._formatBooleanSelect(item), [
      {
        label: 'Inherit',
        value: 'INHERIT',
      },
      {
        label: 'True',
        value: 'TRUE',
      }, {
        label: 'False',
        value: 'FALSE',
      },
    ]);
  });

  test('fires page-error', done => {
    repoStub.restore();

    element.repo = 'test';

    const response = {status: 404};
    sinon.stub(
        element.$.restAPI, 'getProjectConfig').callsFake((repo, errFn) => {
      errFn(response);
    });
    element.addEventListener('page-error', e => {
      assert.deepEqual(e.detail.response, response);
      done();
    });

    element._loadRepo();
  });

  suite('admin', () => {
    setup(() => {
      element.repo = REPO;
      sinon.stub(element, '_getLoggedIn')
          .callsFake(() => Promise.resolve(true));
      sinon.stub(
          element.$.restAPI,
          'getRepoAccess')
          .callsFake(() => Promise.resolve({'test-repo': {is_owner: true}}));
    });

    test('all form elements are enabled', done => {
      element._loadRepo().then(() => {
        flush();
        const formFields = getFormFields();
        for (const field of formFields) {
          assert.isFalse(field.hasAttribute('disabled'));
        }
        assert.isFalse(element._loading);
        done();
      });
    });

    test('state gets set correctly', done => {
      element._loadRepo().then(() => {
        assert.equal(element._repoConfig.state, 'ACTIVE');
        assert.equal(element.$.stateSelect.bindValue, 'ACTIVE');
        done();
      });
    });

    test('inherited submit type value is calculated correctly', done => {
      element
          ._loadRepo().then(() => {
            const sel = element.$.submitTypeSelect;
            assert.equal(sel.bindValue, 'INHERIT');
            assert.equal(
                sel.nativeSelect.options[0].text,
                'Inherit (Merge if necessary)'
            );
            done();
          });
    });

    test('fields update and save correctly', () => {
      const configInputObj = {
        description: 'new description',
        use_contributor_agreements: 'TRUE',
        use_content_merge: 'TRUE',
        use_signed_off_by: 'TRUE',
        create_new_change_for_all_not_in_target: 'TRUE',
        require_change_id: 'TRUE',
        enable_signed_push: 'TRUE',
        require_signed_push: 'TRUE',
        reject_implicit_merges: 'TRUE',
        private_by_default: 'TRUE',
        match_author_to_committer_date: 'TRUE',
        reject_empty_commit: 'TRUE',
        max_object_size_limit: 10,
        submit_type: 'FAST_FORWARD_ONLY',
        state: 'READ_ONLY',
        enable_reviewer_by_email: 'TRUE',
      };

      const saveStub = sinon.stub(element.$.restAPI, 'saveRepoConfig')
          .callsFake(() => Promise.resolve({}));

      const button = element.root.querySelector('gr-button');

      return element._loadRepo().then(() => {
        assert.isTrue(button.hasAttribute('disabled'));
        assert.isFalse(element.$.Title.classList.contains('edited'));
        element.$.descriptionInput.bindValue = configInputObj.description;
        element.$.stateSelect.bindValue = configInputObj.state;
        element.$.submitTypeSelect.bindValue = configInputObj.submit_type;
        element.$.contentMergeSelect.bindValue =
            configInputObj.use_content_merge;
        element.$.newChangeSelect.bindValue =
            configInputObj.create_new_change_for_all_not_in_target;
        element.$.requireChangeIdSelect.bindValue =
            configInputObj.require_change_id;
        element.$.enableSignedPush.bindValue =
            configInputObj.enable_signed_push;
        element.$.requireSignedPush.bindValue =
            configInputObj.require_signed_push;
        element.$.rejectImplicitMergesSelect.bindValue =
            configInputObj.reject_implicit_merges;
        element.$.setAllnewChangesPrivateByDefaultSelect.bindValue =
            configInputObj.private_by_default;
        element.$.matchAuthoredDateWithCommitterDateSelect.bindValue =
            configInputObj.match_author_to_committer_date;
        const inputElement = PolymerElement ?
          element.$.maxGitObjSizeIronInput : element.$.maxGitObjSizeInput;
        inputElement.bindValue = configInputObj.max_object_size_limit;
        element.$.contributorAgreementSelect.bindValue =
            configInputObj.use_contributor_agreements;
        element.$.useSignedOffBySelect.bindValue =
            configInputObj.use_signed_off_by;
        element.$.rejectEmptyCommitSelect.bindValue =
            configInputObj.reject_empty_commit;
        element.$.unRegisteredCcSelect.bindValue =
            configInputObj.enable_reviewer_by_email;

        assert.isFalse(button.hasAttribute('disabled'));
        assert.isTrue(element.$.configurations.classList.contains('edited'));

        const formattedObj =
            element._formatRepoConfigForSave(element._repoConfig);
        assert.deepEqual(formattedObj, configInputObj);

        return element._handleSaveRepoConfig().then(() => {
          assert.isTrue(button.hasAttribute('disabled'));
          assert.isFalse(element.$.Title.classList.contains('edited'));
          assert.isTrue(saveStub.lastCall.calledWithExactly(REPO,
              configInputObj));
        });
      });
    });
  });
});

