import {installTimerService} from '#service/timer-impl';

import {FocusHistory} from '../../src/focus-history';

describes.sandboxed('FocusHistory', {}, (env) => {
  let clock;
  let testDoc;
  let eventListeners;
  let testWindow;
  let windowEventListeners;
  let focusHistory;

  beforeEach(() => {
    clock = env.sandbox.useFakeTimers();

    eventListeners = {};
    testDoc = {
      addEventListener: (eventType, handler, capture) => {
        if (!capture) {
          throw new Error('the focus listener must be capture');
        }
        eventListeners[eventType] = handler;
      },
    };

    windowEventListeners = {};
    testWindow = {
      document: testDoc,
      addEventListener: (eventType, handler) => {
        windowEventListeners[eventType] = handler;
      },
      setTimeout: window.setTimeout,
      clearTimeout: window.clearTimeout,
      Promise: window.Promise,
    };
    installTimerService(testWindow);
    focusHistory = new FocusHistory(testWindow, 10000);
  });

  it('should subscribe to focus events', () => {
    expect(eventListeners['focus']).to.exist;
    expect(windowEventListeners['blur']).to.exist;
    expect(focusHistory.getLast()).to.be.null;
  });

  it('should push focused elements with timestamp', () => {
    const el1 = document.createElement('div');
    const el2 = document.createElement('div');
    eventListeners['focus']({target: el1});
    clock.tick(100);
    eventListeners['focus']({target: el2});

    expect(focusHistory.getLast()).to.equal(el2);
    expect(focusHistory.history_.length).to.equal(2);
    expect(focusHistory.history_[0]).to.deep.equal({el: el1, time: 0});
    expect(focusHistory.history_[1]).to.deep.equal({el: el1, time: 100});
  });

  it('should push focused elements with timestamp after window.blur', () => {
    windowEventListeners['blur']({});
    expect(focusHistory.history_.length).to.equal(0);

    const el1 = document.createElement('div');
    testDoc.activeElement = el1;
    clock.tick(1000);
    expect(focusHistory.history_.length).to.equal(1);
    expect(focusHistory.history_[0].el).to.deep.equal(el1);
  });

  it('should push and purge', () => {
    const el1 = document.createElement('div');
    const el2 = document.createElement('div');
    eventListeners['focus']({target: el1});
    clock.tick(100000);
    eventListeners['focus']({target: el2});

    expect(focusHistory.getLast()).to.equal(el2);
    expect(focusHistory.history_.length).to.equal(1);
    expect(focusHistory.history_[0]).to.deep.equal({el: el1, time: 100000});
  });

  it('should replace second push with a new timestamp', () => {
    const el1 = document.createElement('div');
    const el2 = document.createElement('div');
    eventListeners['focus']({target: el1});
    clock.tick(100);
    eventListeners['focus']({target: el2});
    expect(focusHistory.history_.length).to.equal(2);

    clock.tick(100);
    eventListeners['focus']({target: el2});
    expect(focusHistory.history_.length).to.equal(2);
    expect(focusHistory.getLast()).to.equal(el2);
    expect(focusHistory.history_[0]).to.deep.equal({el: el1, time: 0});
    expect(focusHistory.history_[1]).to.deep.equal({el: el1, time: 200});
  });

  it('should purge elements before timestamp', () => {
    const el1 = document.createElement('div');
    const el2 = document.createElement('div');
    clock.tick(100);
    eventListeners['focus']({target: el1}); // time=100
    clock.tick(100);
    eventListeners['focus']({target: el2}); // time=200

    focusHistory.purgeBefore(50);
    expect(focusHistory.history_.length).to.equal(2);

    focusHistory.purgeBefore(100);
    expect(focusHistory.history_.length).to.equal(2);

    focusHistory.purgeBefore(101);
    expect(focusHistory.history_.length).to.equal(1);
    expect(focusHistory.history_[0].el).to.equal(el2);

    focusHistory.purgeBefore(201);
    expect(focusHistory.history_.length).to.equal(0);
  });

  it('should return false when nothing matches descendants', () => {
    const el0 = document.createElement('div');
    expect(focusHistory.hasDescendantsOf(el0)).to.be.false;

    eventListeners['focus']({target: document.createElement('div')});
    expect(focusHistory.hasDescendantsOf(el0)).to.be.false;
  });

  it('should check active element for descendants', () => {
    const el0 = document.createElement('div');
    const el01 = document.createElement('div');
    el0.appendChild(el01);

    testDoc.activeElement = el0;
    expect(focusHistory.hasDescendantsOf(el0)).to.be.true;
    expect(focusHistory.hasDescendantsOf(document.createElement('div'))).to.be
      .false;

    testDoc.activeElement = el01;
    expect(focusHistory.hasDescendantsOf(el0)).to.be.true;
  });

  it('should check history descendants', () => {
    const el0 = document.createElement('div');
    const el01 = document.createElement('div');
    el0.appendChild(el01);
    eventListeners['focus']({target: el01});
    expect(focusHistory.hasDescendantsOf(el0)).to.be.true;
    expect(focusHistory.hasDescendantsOf(el01)).to.be.true;
    expect(focusHistory.hasDescendantsOf(document.createElement('div'))).to.be
      .false;
  });
});
