/*****************************************************************************
 * Open MCT, Copyright (c) 2014-2024, United States Government
 * as represented by the Administrator of the National Aeronautics and Space
 * Administration. All rights reserved.
 *
 * Open MCT is 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.
 *
 * Open MCT includes source code licensed under additional open source
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 * this source code distribution or the Licensing information page available
 * at runtime from the About dialog for additional information.
 *****************************************************************************/
import { debounce } from 'lodash';
import { createMouseEvent, createOpenMct, resetApplicationState } from 'utils/testing';
import { nextTick } from 'vue';

describe('EditPropertiesAction plugin', () => {
  let editPropertiesAction;
  let openmct;
  let element;

  beforeEach((done) => {
    element = document.createElement('div');
    element.style.display = 'block';
    element.style.width = '1920px';
    element.style.height = '1080px';

    openmct = createOpenMct();
    openmct.on('start', done);
    openmct.startHeadless(element);

    editPropertiesAction = openmct.actions.getAction('properties');
  });

  afterEach(() => {
    editPropertiesAction = null;

    return resetApplicationState(openmct);
  });

  it('editPropertiesAction exists', () => {
    expect(editPropertiesAction.key).toEqual('properties');
  });

  it('edit properties action applies to only persistable objects', () => {
    spyOn(openmct.objects, 'isPersistable').and.returnValue(true);

    const domainObject = {
      name: 'mock folder',
      type: 'folder',
      identifier: {
        key: 'mock-folder',
        namespace: ''
      },
      composition: []
    };
    const isApplicableTo = editPropertiesAction.appliesTo([domainObject]);
    expect(isApplicableTo).toBe(true);
  });

  it('edit properties action does not apply to non persistable objects', () => {
    spyOn(openmct.objects, 'isPersistable').and.returnValue(false);

    const domainObject = {
      name: 'mock folder',
      type: 'folder',
      identifier: {
        key: 'mock-folder',
        namespace: ''
      },
      composition: []
    };
    const isApplicableTo = editPropertiesAction.appliesTo([domainObject]);
    expect(isApplicableTo).toBe(false);
  });

  it('edit properties action when invoked shows form', (done) => {
    const domainObject = {
      name: 'mock folder',
      notes: 'mock notes',
      type: 'folder',
      identifier: {
        key: 'mock-folder',
        namespace: ''
      },
      modified: 1643065068597,
      persisted: 1643065068600,
      composition: []
    };

    editPropertiesAction
      .invoke([domainObject])
      .then(() => {
        done();
      })
      .catch(() => {
        done();
      });

    nextTick(() => {
      const form = document.querySelector('.js-form');
      const title = form.querySelector('input');
      expect(title.value).toEqual(domainObject.name);

      const notes = form.querySelector('textArea');
      expect(notes.value).toEqual(domainObject.notes);

      const buttons = form.querySelectorAll('button');
      expect(buttons[0].textContent.trim()).toEqual('Ok');
      expect(buttons[1].textContent.trim()).toEqual('Cancel');

      const clickEvent = createMouseEvent('click');
      buttons[1].dispatchEvent(clickEvent);
    });
  });

  it('edit properties action saves changes', (done) => {
    const oldName = 'mock folder';
    const newName = 'renamed mock folder';
    const domainObject = {
      name: oldName,
      notes: 'mock notes',
      type: 'folder',
      identifier: {
        key: 'mock-folder',
        namespace: ''
      },
      modified: 1643065068597,
      persisted: 1643065068600,
      composition: []
    };
    let unObserve;

    function callback(newObject) {
      expect(newObject.name).not.toEqual(oldName);
      expect(newObject.name).toEqual(newName);

      unObserve();
      done();
    }

    const deBouncedCallback = debounce(callback, 300);
    unObserve = openmct.objects.observe(domainObject, '*', deBouncedCallback);

    editPropertiesAction.invoke([domainObject]);

    nextTick(() => {
      const form = document.querySelector('.js-form');
      const title = form.querySelector('input');
      const notes = form.querySelector('textArea');

      const buttons = form.querySelectorAll('button');
      expect(buttons[0].textContent.trim()).toEqual('Ok');
      expect(buttons[1].textContent.trim()).toEqual('Cancel');

      expect(title.value).toEqual(domainObject.name);
      expect(notes.value).toEqual(domainObject.notes);

      // change input field value and dispatch event for it
      title.focus();
      title.value = newName;
      title.dispatchEvent(new Event('input'));
      title.blur();

      const clickEvent = createMouseEvent('click');
      buttons[0].dispatchEvent(clickEvent);
    });
  });

  it('edit properties action discards changes', (done) => {
    const name = 'mock folder';
    const domainObject = {
      name,
      notes: 'mock notes',
      type: 'folder',
      identifier: {
        key: 'mock-folder',
        namespace: ''
      },
      modified: 1643065068597,
      persisted: 1643065068600,
      composition: []
    };

    editPropertiesAction
      .invoke([domainObject])
      .then(() => {
        expect(domainObject.name).toEqual(name);
        done();
      })
      .catch(() => {
        expect(domainObject.name).toEqual(name);
        done();
      });

    const form = document.querySelector('.js-form');
    const buttons = form.querySelectorAll('button');
    const clickEvent = createMouseEvent('click');
    buttons[1].dispatchEvent(clickEvent);
  });
});
