// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import * as Protocol from '../../generated/protocol.js';
import {raf} from '../../testing/DOMHelpers.js';
import {describeWithMockConnection} from '../../testing/MockConnection.js';

import * as Resources from './application.js';

import View = Resources.SharedStorageEventsView;

describeWithMockConnection('SharedStorageEventsView', () => {
  const TEST_ORIGIN_A = 'http://a.test';
  const TEST_ORIGIN_B = 'http://b.test';
  const TEST_ORIGIN_C = 'http://c.test';

  const ID_A = 'AA' as Protocol.Page.FrameId;
  const ID_B = 'BB' as Protocol.Page.FrameId;
  const EMPTY_ID = '' as Protocol.Page.FrameId;

  const EVENTS = [
    {
      accessTime: 0,
      type: Protocol.Storage.SharedStorageAccessType.DocumentAppend,
      mainFrameId: ID_A,
      ownerOrigin: TEST_ORIGIN_A,
      params: {key: 'key0', value: 'value0'} as Protocol.Storage.SharedStorageAccessParams,
    },
    {
      accessTime: 10,
      type: Protocol.Storage.SharedStorageAccessType.WorkletGet,
      mainFrameId: ID_A,
      ownerOrigin: TEST_ORIGIN_A,
      params: {key: 'key0'} as Protocol.Storage.SharedStorageAccessParams,
    },
    {
      accessTime: 15,
      type: Protocol.Storage.SharedStorageAccessType.WorkletLength,
      mainFrameId: ID_A,
      ownerOrigin: TEST_ORIGIN_B,
      params: {} as Protocol.Storage.SharedStorageAccessParams,
    },
    {
      accessTime: 20,
      type: Protocol.Storage.SharedStorageAccessType.DocumentClear,
      mainFrameId: ID_A,
      ownerOrigin: TEST_ORIGIN_B,
      params: {} as Protocol.Storage.SharedStorageAccessParams,
    },
    {
      accessTime: 100,
      type: Protocol.Storage.SharedStorageAccessType.WorkletSet,
      mainFrameId: ID_A,
      ownerOrigin: TEST_ORIGIN_C,
      params: {key: 'key0', value: 'value1', ignoreIfPresent: true} as Protocol.Storage.SharedStorageAccessParams,
    },
    {
      accessTime: 150,
      type: Protocol.Storage.SharedStorageAccessType.WorkletRemainingBudget,
      mainFrameId: ID_A,
      ownerOrigin: TEST_ORIGIN_C,
      params: {} as Protocol.Storage.SharedStorageAccessParams,
    },
  ];

  const MULTI_PAGE_EVENTS = [
    {
      accessTime: 0,
      type: Protocol.Storage.SharedStorageAccessType.DocumentAppend,
      mainFrameId: ID_A,
      ownerOrigin: TEST_ORIGIN_A,
      params: {key: 'key0', value: 'value0'} as Protocol.Storage.SharedStorageAccessParams,
    },
    {
      accessTime: 10,
      type: Protocol.Storage.SharedStorageAccessType.WorkletGet,
      mainFrameId: ID_B,
      ownerOrigin: TEST_ORIGIN_A,
      params: {key: 'key0'} as Protocol.Storage.SharedStorageAccessParams,
    },
    {
      accessTime: 15,
      type: Protocol.Storage.SharedStorageAccessType.WorkletLength,
      mainFrameId: ID_A,
      ownerOrigin: TEST_ORIGIN_B,
      params: {} as Protocol.Storage.SharedStorageAccessParams,
    },
    {
      accessTime: 20,
      type: Protocol.Storage.SharedStorageAccessType.DocumentClear,
      mainFrameId: EMPTY_ID,
      ownerOrigin: TEST_ORIGIN_B,
      params: {} as Protocol.Storage.SharedStorageAccessParams,
    },
    {
      accessTime: 100,
      type: Protocol.Storage.SharedStorageAccessType.WorkletSet,
      mainFrameId: EMPTY_ID,
      ownerOrigin: TEST_ORIGIN_C,
      params: {key: 'key0', value: 'value1', ignoreIfPresent: true} as Protocol.Storage.SharedStorageAccessParams,
    },
    {
      accessTime: 150,
      type: Protocol.Storage.SharedStorageAccessType.WorkletRemainingBudget,
      mainFrameId: ID_B,
      ownerOrigin: TEST_ORIGIN_C,
      params: {} as Protocol.Storage.SharedStorageAccessParams,
    },
  ];

  const PAGE_A_EVENTS = [
    {
      accessTime: 0,
      type: Protocol.Storage.SharedStorageAccessType.DocumentAppend,
      mainFrameId: ID_A,
      ownerOrigin: TEST_ORIGIN_A,
      params: {key: 'key0', value: 'value0'} as Protocol.Storage.SharedStorageAccessParams,
    },
    {
      accessTime: 15,
      type: Protocol.Storage.SharedStorageAccessType.WorkletLength,
      mainFrameId: ID_A,
      ownerOrigin: TEST_ORIGIN_B,
      params: {} as Protocol.Storage.SharedStorageAccessParams,
    },
  ];

  it('records events', () => {
    const view = new View.SharedStorageEventsView();
    view.setDefaultIdForTesting(ID_A);
    for (const event of EVENTS) {
      view.addEvent(event);
    }
    assert.deepEqual(view.getEventsForTesting(), EVENTS);
  });

  it('ignores duplicates', () => {
    const view = new View.SharedStorageEventsView();
    view.setDefaultIdForTesting(ID_A);
    for (const event of EVENTS) {
      view.addEvent(event);
    }
    view.addEvent(EVENTS[0]);
    assert.deepEqual(view.getEventsForTesting(), EVENTS);
  });

  it('initially has placeholder sidebar', () => {
    const view = new View.SharedStorageEventsView();
    assert.notDeepEqual(view.sidebarWidget()?.constructor.name, 'SearchableView');
    assert.deepEqual(view.sidebarWidget()?.contentElement.firstChild?.textContent, 'No shared storage event selected');
  });

  it('updates sidebarWidget upon receiving cellFocusedEvent', async () => {
    const view = new View.SharedStorageEventsView();
    view.setDefaultIdForTesting(ID_A);
    for (const event of EVENTS) {
      view.addEvent(event);
    }
    const grid = view.getSharedStorageAccessGridForTesting();

    // Use a spy to assert that the sidebar preview pane gets updated when expected.
    const spy = sinon.spy(view, 'setSidebarWidget');
    assert.isTrue(spy.notCalled);
    grid.dispatchEvent(new CustomEvent<Protocol.Storage.SharedStorageAccessedEvent>('select', {detail: EVENTS[0]}));
    await raf();
    assert.isTrue(spy.calledOnce);
    assert.deepEqual(view.sidebarWidget()?.constructor.name, 'SearchableView');
  });

  it('clears sidebarWidget upon clearEvents', async () => {
    const view = new View.SharedStorageEventsView();
    view.setDefaultIdForTesting(ID_A);
    for (const event of EVENTS) {
      view.addEvent(event);
    }
    const grid = view.getSharedStorageAccessGridForTesting();

    // Use a spy to assert that the sidebar preview pane gets updated when expected.
    const spy = sinon.spy(view, 'setSidebarWidget');
    assert.isTrue(spy.notCalled);
    grid.dispatchEvent(new CustomEvent<Protocol.Storage.SharedStorageAccessedEvent>('select', {detail: EVENTS[0]}));
    await raf();
    assert.isTrue(spy.calledOnce);
    assert.deepEqual(view.sidebarWidget()?.constructor.name, 'SearchableView');
    view.clearEvents();
    assert.isTrue(spy.calledTwice);
    assert.notDeepEqual(view.sidebarWidget()?.constructor.name, 'SearchableView');
    assert.deepEqual(view.sidebarWidget()?.contentElement.firstChild?.textContent, 'No shared storage event selected');
  });

  it('records events only from the target page', () => {
    const view = new View.SharedStorageEventsView();
    view.setDefaultIdForTesting(ID_A);
    for (const event of MULTI_PAGE_EVENTS) {
      view.addEvent(event);
    }
    assert.deepEqual(view.getEventsForTesting(), PAGE_A_EVENTS);
  });
});
