/* eslint-disable no-await-in-loop */
import { assert, fixture, html, oneEvent } from '@open-wc/testing';
import sinon from 'sinon';
import { 
  FolderKind,
  IFolder, 
  ProjectKind
} from '@api-client/core/build/browser.js';
import type FilePicker from '../../../src/elements/store/FilePicker.element.js';
import type UiListItem from '../../../src/ui/list/UiListItem.js';
import env from '../../env.js';
import HttpHelper from '../../helpers/StoreHelper.js';
import type { FilePickerClosingReason } from '../../../src/elements/store/FilePicker.element.js';
import '../../../src/define/store/file-picker.js';

describe('elements', () => {
  describe('store', () => {
    describe('FilePicker', () => {
      async function manualFixture(): Promise<FilePicker> {
        return fixture(html`<file-picker .debounceTimeout="${0}" manualQuery></file-picker>`);
      }

      let helper: HttpHelper;
      let baseUri: string;
      before(async () => {
        baseUri = env.store.single;
        helper = new HttpHelper(baseUri);
        await helper.initializeSdk();
        await helper.addUser({ defaultUser: true });
        await helper.addSpace({ defaultSpace: true });
      });

      after(async () => {
        await helper.clearFiles();
        await helper.clearSpaces();
        await helper.clearPermissions();
        await helper.clearSessions();
      });

      describe('file list (store has files)', () => {
        let folders: IFolder[];
        let emptyFolder: IFolder;

        before(async () => {
          folders = await helper.generateFolders(10);
          await helper.generateProjects(20);
          for (const parent of folders) {
            await helper.addFolder({ parent: parent.key });
            await helper.addProject({ parent: parent.key });
          }
          // empty folder without children
          emptyFolder = await helper.addFolder();
        });

        after(async () => {
          await helper.clearFiles();
          await helper.clearPermissions();
        });

        it('renders the list of files', async () => {
          const element = await manualFixture();
          element.limit = 20;
          await element.fs.queryPage();
          await element.updateComplete;
          assert.lengthOf(element.fs.files, 20, 'has the page of results');
          assert.typeOf(element.fs.cursor, 'string', 'has the page cursor');
          assert.isUndefined(element.fs.breadcrumbs, 'has no breadcrumbs for the root');
          assert.isUndefined(element.selectedKey, 'has no selectedKey');
          const list = element.shadowRoot!.querySelector('ui-list')!;
          const items = Array.from(list.querySelectorAll('ui-list-item'));
          assert.lengthOf(items, 20, 'renders the list of files');
        });

        it('enters an empty folder', async () => {
          const element = await manualFixture();
          await element.fs.queryPage();
          await element.updateComplete;
          const eventPromise = oneEvent(element, 'querycomplete');
          const item = element.shadowRoot!.querySelector(`ui-list-item[data-key="${emptyFolder.key}"]`) as UiListItem;
          item.click();
          assert.equal(element.fs.parent, emptyFolder.key, 'selects a parent');
          assert.deepEqual(element.fs.files, [], 'resets files');
          assert.isUndefined(element.fs.cursor, 'resets page cursor');
          await eventPromise;
          assert.deepEqual(element.fs.files, [], 'has no files in the sub-folder');
          const node = element.shadowRoot!.querySelector('.no-files')!;
          assert.ok(node, 'has the no-files message');
          assert.equal(node.textContent!.trim(), 'No files in this view.');
        });

        it('enters a folder with files', async () => {
          const element = await manualFixture();
          await element.fs.queryPage();
          await element.updateComplete;
          const cursorBefore = element.fs.cursor;
          const [f1] = folders;
          const item = element.shadowRoot!.querySelector(`ui-list-item[data-key="${f1.key}"]`) as UiListItem;
          item.click();
          await oneEvent(element, 'querycomplete');
          await element.updateComplete;
          assert.equal(element.fs.parent, f1.key, 'selects a parent');
          assert.lengthOf(element.fs.files, 2, 'has both files');
          assert.typeOf(element.fs.cursor, 'string', 'has the page cursor');
          assert.notEqual(element.fs.cursor, cursorBefore, 'has different cursor');
          const node = element.shadowRoot!.querySelector('.no-files');
          assert.notOk(node, 'has no empty message');

          const list = element.shadowRoot!.querySelector('ui-list')!;
          const items = Array.from(list.querySelectorAll('ui-list-item'));
          assert.lengthOf(items, 2, 'renders the list of files');
        });

        it('goes to a deep child', async () => {
          const element = await manualFixture();
          await element.fs.queryPage();
          await element.updateComplete;
          const [f1] = folders;
          const item = element.shadowRoot!.querySelector(`ui-list-item[data-key="${f1.key}"]`) as UiListItem;
          item.click();
          await oneEvent(element, 'querycomplete');
          await element.updateComplete;
          const item2 = element.shadowRoot!.querySelector(`ui-list-item[data-kind="${FolderKind}"]`) as UiListItem;
          item2.click();
          await oneEvent(element, 'querycomplete');
          await element.updateComplete;
          assert.deepEqual(element.fs.files, [], 'has no files in the sub-folder');
          const node = element.shadowRoot!.querySelector('.no-files')!;
          assert.ok(node, 'has the no-files message');
          assert.equal(node.textContent!.trim(), 'No files in this view.');
        });

        it('moves to the parent folder', async () => {
          const [f1] = folders;
          const files = await helper.sdk.file.list(helper.space!.key, [], { parent: f1.key });
          
          const child = files.items.find(i => i.kind === FolderKind) as IFolder;
          const element = await manualFixture();
          element.limit = 20;
          element.folder = child.key;
          await element.fs.queryPage();
          await element.updateComplete;

          const back = element.shadowRoot!.querySelector('.back-button') as HTMLElement;
          back.click();
          await oneEvent(element, 'querycomplete');
          await element.updateComplete;

          assert.equal(element.folder, f1.key, 'switches to the parent folder');
          const f1i = element.shadowRoot!.querySelector(`ui-list-item[data-key="${files.items[0].key}"]`) as UiListItem;
          const f2i = element.shadowRoot!.querySelector(`ui-list-item[data-key="${files.items[1].key}"]`) as UiListItem;
          assert.ok(f1i, 'has the first file');
          assert.ok(f2i, 'has the other file');

          back.click();
          await oneEvent(element, 'querycomplete');
          await element.updateComplete;

          assert.isUndefined(element.folder, 'goes up to the root');
          const list = element.shadowRoot!.querySelector('ui-list')!;
          const items = Array.from(list.querySelectorAll('ui-list-item'));
          assert.lengthOf(items, 20, 'renders the list of files');
        });

        it('render select button disabled', async () => {
          const element = await manualFixture();
          await element.fs.queryPage();
          await element.updateComplete;

          const button = element.shadowRoot!.querySelector('[value="select"]')!;
          assert.ok(button, 'has the button');
          assert.isTrue(button.hasAttribute('disabled'), 'the button is disabled');
        });

        it('render cancel button', async () => {
          const element = await manualFixture();
          await element.fs.queryPage();
          await element.updateComplete;

          const button = element.shadowRoot!.querySelector('[value="cancel"]')!;
          assert.ok(button, 'has the button');
          assert.isFalse(button.hasAttribute('disabled'), 'the button is not disabled');
        });

        it('selects a file', async () => {
          const element = await manualFixture();
          await element.fs.queryPage();
          await element.updateComplete;

          const item = element.shadowRoot!.querySelector(`ui-list-item[data-kind="${ProjectKind}"]`) as UiListItem;
          item.click();

          await element.updateComplete;
          const button = element.shadowRoot!.querySelector('[value="select"]')!;
          assert.isFalse(button.hasAttribute('disabled'), 'the select button is not disabled');
        });

        it('dispatches the close event with the selected file', async () => {
          const element = await manualFixture();
          await element.fs.queryPage();
          await element.updateComplete;

          const item = element.shadowRoot!.querySelector(`ui-list-item[data-kind="${ProjectKind}"]`) as UiListItem;
          item.click();

          await element.updateComplete;

          const spy = sinon.spy();
          element.addEventListener('close', spy);
          const button = element.shadowRoot!.querySelector('[value="select"]') as HTMLElement;
          button.click();

          assert.isTrue(spy.calledOnce, 'event is dispatched once');
          const event = spy.args[0][0] as CustomEvent<FilePickerClosingReason>;
          assert.isFalse(event.detail.canceled, 'the dialog was not canceled')
          assert.ok(event.detail.file, 'the detail has the file')
        });

        it('cancels the dialog', async () => {
          const element = await manualFixture();
          await element.fs.queryPage();
          await element.updateComplete;

          const item = element.shadowRoot!.querySelector(`ui-list-item[data-kind="${ProjectKind}"]`) as UiListItem;
          item.click();

          await element.updateComplete;

          const spy = sinon.spy();
          element.addEventListener('close', spy);
          const button = element.shadowRoot!.querySelector('[value="cancel"]') as HTMLElement;
          button.click();

          assert.isTrue(spy.calledOnce, 'event is dispatched once');
          const event = spy.args[0][0] as CustomEvent<FilePickerClosingReason>;
          assert.isTrue(event.detail.canceled, 'the dialog was canceled')
          assert.notOk(event.detail.file, 'the detail has no file')
        });
      });
    });
  });
});
