/*
 * notion-enhancer core: menu
 * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
 * (https://notion-enhancer.github.io/) under the MIT license
 */

'use strict';

const _id = 'a6621988-551d-495a-97d8-3c568bca2e9e';
import { env, storage, web, fmt, fs, registry, regexers } from '../../api/_.mjs';

document.querySelector('img[data-notion]').addEventListener('click', env.focusNotion);

import * as router from './router.js';

const components = {};
components.card = async (mod) => {
  const $card = web.createElement(web.html`
  <article class="library--card" data-mod='${mod.id}'>
  ${
    mod.preview
      ? web.html`<img
          alt=""
          class="library--preview"
          src="${web.escapeHtml(mod.preview)}"
        />`
      : ''
  }
  <div>
    <label
      for="enable--${web.escapeHtml(mod.id)}"
      class="library--title library--toggle_label"
    >
      <input type="checkbox" id="enable--${web.escapeHtml(mod.id)}"
      ${(await registry.isEnabled(mod.id)) ? 'checked' : ''}/>
      <h2>
        <span>
          ${web.escapeHtml(mod.name)}
          <span class="library--version">v${web.escapeHtml(mod.version)}</span>
        </span>
        ${
          registry.CORE.includes(mod.id) ? '' : web.html`<span class="library--toggle"></span>`
        }
      </h2>
    </label>
    <ul class="library--tags">
      ${mod.tags.map((tag) => web.html`<li>#${web.escapeHtml(tag)}</li>`).join('')}
    </ul>
    <p class="library--description markdown">${fmt.md.renderInline(mod.description)}</p>
    <ul class="library--authors">
      ${mod.authors
        .map(
          (author) =>
            web.html`
            <li>
              <a href="${web.escapeHtml(author.url)}">
                <img alt="" src="${web.escapeHtml(author.icon)}" />
                <span>${web.escapeHtml(author.name)}</span>
              </a>
            </li>`
        )
        .join('')}
    </ul>
    <p class="library--expand">
      <a href="?view=mod&id=${web.escapeHtml(mod.id)}">
        <span><i data-icon="fa/solid/long-arrow-alt-right"></i></span>
        <span>settings & documentation</span>
      </a>
    </p>
  </div>
  </article>`);
  $card.querySelector('.library--title input').addEventListener('change', async (event) => {
    storage.set('_mods', mod.id, event.target.checked);
  });
  return $card;
};
components.options = {
  async toggle(id, { key, label, tooltip }) {
    const state = await storage.get(id, key),
      opt = web.createElement(web.html`
      <label
        for="toggle--${web.escapeHtml(`${id}.${key}`)}"
        class="library--toggle_label"
      >
        <input type="checkbox" id="toggle--${web.escapeHtml(`${id}.${key}`)}"
        ${state ? 'checked' : ''}/>
        <p>
          <span data-tooltip>${web.escapeHtml(label)}
          ${tooltip ? web.html`<i data-icon="fa/solid/question-circle"></i>` : ''}</span>
          <span class="library--toggle"></span>
        </p>
      </label>`);
    opt.addEventListener('change', (event) => storage.set(id, key, event.target.checked));
    if (tooltip) web.addTooltip(opt.querySelector('[data-tooltip]'), tooltip);
    return opt;
  },
  async select(id, { key, label, tooltip, values }) {
    const state = await storage.get(id, key),
      opt = web.createElement(web.html`
      <label
        for="select--${web.escapeHtml(`${id}.${key}`)}"
        class="library--select_label"
      >
        <p><span data-tooltip>${web.escapeHtml(label)}
        ${tooltip ? web.html`<i data-icon="fa/solid/question-circle"></i>` : ''}</span></p>
        <p class="library--select">
          <span><i data-icon="fa/solid/caret-down"></i></span>
          <select id="select--${web.escapeHtml(`${id}.${key}`)}">
            ${values.map(
              (value) =>
                web.html`<option value="${web.escapeHtml(value)}"
                ${value === state ? 'selected' : ''}>
                ${web.escapeHtml(value)}</option>`
            )}
          </select>
        </p>
      </label>`);
    opt.addEventListener('change', (event) => storage.set(id, key, event.target.value));
    if (tooltip) web.addTooltip(opt.querySelector('[data-tooltip]'), tooltip);
    return opt;
  },
  async text(id, { key, label, tooltip }) {
    const state = await storage.get(id, key),
      opt = web.createElement(web.html`
      <label
        for="text--${web.escapeHtml(`${id}.${key}`)}"
        class="library--text_label"
      >
        <p><span data-tooltip>${web.escapeHtml(label)}
        ${tooltip ? web.html`<i data-icon="fa/solid/question-circle"></i>` : ''}</span></p>
        <textarea id="text--${web.escapeHtml(`${id}.${key}`)}"
        rows="1">${web.escapeHtml(state)}</textarea>
      </label>`);
    opt.querySelector('textarea').addEventListener('input', (event) => {
      event.target.style.removeProperty('--txt--scroll-height');
      event.target.style.setProperty(
        '--txt--scroll-height',
        event.target.scrollHeight + 1 + 'px'
      );
    });
    opt.addEventListener('change', (event) => storage.set(id, key, event.target.value));
    if (tooltip) web.addTooltip(opt.querySelector('[data-tooltip]'), tooltip);
    return opt;
  },
  async number(id, { key, label, tooltip }) {
    const state = await storage.get(id, key),
      opt = web.createElement(web.html`
      <label
        for="number--${web.escapeHtml(`${id}.${key}`)}"
        class="library--number_label"
      >
        <p><span data-tooltip>${web.escapeHtml(label)}
        ${tooltip ? web.html`<i data-icon="fa/solid/question-circle"></i>` : ''}</span></p>
        <input id="number--${web.escapeHtml(`${id}.${key}`)}"
        type="number" value="${web.escapeHtml(state.toString())}"/>
      </label>`);
    opt.addEventListener('change', (event) => storage.set(id, key, event.target.value));
    if (tooltip) web.addTooltip(opt.querySelector('[data-tooltip]'), tooltip);
    return opt;
  },
  async color(id, { key, label, tooltip }) {
    const state = await storage.get(id, key),
      opt = web.createElement(web.html`
      <label for="color--${web.escapeHtml(`${id}.${key}`)}" class="library--color_label">
        <p class="library--color_title">
        <span data-tooltip>${web.escapeHtml(label)}
          <i data-icon="fa/solid/question-circle"></i>
        </span>
        <p class="library--color">
          <span><i data-icon="fa/solid/eye-dropper"></i></span>
          <input type="text" id="color--${web.escapeHtml(`${id}.${key}`)}"/>
        </p>
      </label>`);
    const $fill = opt.querySelector('input'),
      paintInput = () => {
        $fill.style.background = picker.toBackground();
        $fill.style.color = picker.isLight() ? '#000' : '#fff';
      },
      picker = new fmt.JSColor($fill, {
        value: state,
        previewSize: 0,
        borderRadius: 3,
        borderColor: 'var(--theme--divider)',
        controlBorderColor: 'var(--theme--divider)',
        backgroundColor: 'var(--theme--page)',
        onInput() {
          paintInput();
        },
        onChange() {
          paintInput();
          storage.set(id, key, this.toRGBAString());
        },
      });
    paintInput();
    opt.addEventListener('click', (event) => {
      picker.show();
    });
    if (tooltip) web.addTooltip(opt.querySelector('[data-tooltip]'), tooltip);
    return opt;
  },
  async file(id, { key, label, tooltip, extensions }) {
    const state = await storage.get(id, key),
      opt = web.createElement(web.html`
      <label
        for="file--${web.escapeHtml(`${id}.${key}`)}"
        class="library--file_label"
      >
        <input
          type="file"
          id="file--${web.escapeHtml(`${id}.${key}`)}"
          ${web.escapeHtml(
            extensions && extensions.length
              ? ` accept=${web.escapeHtml(extensions.join(','))}`
              : ''
          )}
        />
        <p class="library--file_title"><span data-tooltip>${web.escapeHtml(label)}
        <i data-icon="fa/solid/question-circle"></i></span>
        <span class="library--file_remove"><i data-icon="fa/solid/minus"></i></span></p>
        <p class="library--file">
          <span><i data-icon="fa/solid/file"></i></span>
          <span class="library--file_path">${web.escapeHtml(state || 'choose file...')}</span>
        </p>
      </label>`);
    opt.addEventListener('change', (event) => {
      const file = event.target.files[0],
        reader = new FileReader();
      opt.querySelector('.library--file_path').innerText = file.name;
      reader.onload = (progress) => {
        storage.set(id, key, file.name);
        storage.set(id, `_file.${key}`, progress.currentTarget.result);
      };
      reader.readAsText(file);
    });
    opt.querySelector('.library--file_remove').addEventListener(
      'click',
      (event) => {
        event.preventDefault();
        opt.querySelector('input').value = '';
        opt.querySelector('.library--file_path').innerText = 'choose file...';
        storage.set(id, key, undefined);
        storage.set(id, `_file.${key}`, undefined);
      },
      false
    );
    opt.addEventListener('click', (event) => {
      document.documentElement.scrollTop = 0;
    });
    web.addTooltip(
      opt.querySelector('[data-tooltip]'),
      `${tooltip ? `${tooltip}\n\n` : ''}**warning:** ${
        'browser extensions do not have true filesystem access, ' +
        'so file content is only saved on selection. re-select files to apply edits.'
      }`
    );
    return opt;
  },
};

const actionButtons = {
  _reloadTriggered: false,
  async reload($fragment = document) {
    let $reload = $fragment.querySelector('[data-reload]');
    if (!$reload && this._reloadTriggered) {
      $reload = web.createElement(web.html`
      <button class="action--alert" data-reload>
        <span><i data-icon="fa/solid/redo"></i></span>
        <span>reload tabs to apply changes</span>
      </button>`);
      $reload.addEventListener('click', env.reloadTabs);
      $fragment.querySelector('.action--buttons').append($reload);
      await new Promise((res, rej) => requestAnimationFrame(res));
      $reload.dataset.triggered = true;
    }
  },
  async clearFilters($fragment = document) {
    let $clearFilters = $fragment.querySelector('[data-clear-filters]');
    const search = router.getSearch();
    if (search.get('tag') || search.has('enabled') || search.has('disabled')) {
      if (!$clearFilters) {
        $clearFilters = web.createElement(web.html`
        <a class="action--alert" href="?view=library" data-clear-filters>
          <span><i data-icon="fa/solid/times"></i></span>
          <span>clear filters</span>
        </a>`);
        $fragment.querySelector('.action--buttons').append($clearFilters);
        await new Promise((res, rej) => requestAnimationFrame(res));
        $clearFilters.dataset.triggered = true;
      }
    } else if ($clearFilters) $clearFilters.remove();
  },
};
storage.addChangeListener(async (event) => {
  actionButtons._reloadTriggered = true;
  actionButtons.reload();
  router.load();

  if (event.namespace === '_mods' && event.new === true) {
    const enabledTheme = (await registry.get()).find((mod) => mod.id === event.key);
    if (
      enabledTheme.tags.includes('theme') &&
      (await storage.get(_id, 'themes.autoresolve', true))
    ) {
      for (const theme of await registry.get(
        (mod) =>
          mod.tags.includes('theme') &&
          mod.id !== enabledTheme.id &&
          ((mod.tags.includes('dark') && enabledTheme.tags.includes('dark')) ||
            (mod.tags.includes('light') && enabledTheme.tags.includes('light')))
      )) {
        if (document.body.dataset.view === 'library') {
          const $toggle = document.getElementById(`enable--${theme.id}`);
          if ($toggle.checked) $toggle.click();
        } else storage.set('_mods', theme.id, false);
      }
    }
  }
});

router.addView(
  'library',
  async () => {
    const $fragment = web.createFragment(web.html`
    <p class="action--buttons">
      <a href="?view=library&tag=theme">
        <span><i data-icon="fa/solid/palette"></i></span>
        <span>themes</span>
      </a>
      <a href="?view=library&tag=extension">
        <span><i data-icon="fa/solid/plus"></i></span>
        <span>extensions</span>
      </a>
      <a href="?view=library&enabled">
        <span><i data-icon="fa/solid/toggle-on"></i></span>
        <span>enabled</span>
      </a>
      <a href="?view=library&disabled">
        <span><i data-icon="fa/solid/toggle-off"></i></span>
        <span>disabled</span>
      </a>
    </p>`);
    for (const mod of await registry.get(
      (mod) => !mod.environments || mod.environments.includes(env.name)
    )) {
      $fragment.append(await components.card(mod));
    }
    actionButtons.reload($fragment);
    actionButtons.clearFilters($fragment);
    return $fragment;
  },
  async (search = router.getSearch()) => {
    for (const [filter, active] of [
      ['tag=theme', search.get('tag') === 'theme'],
      ['tag=extension', search.get('tag') === 'extension'],
      ['enabled', search.has('enabled')],
      ['disabled', search.has('disabled')],
    ]) {
      document
        .querySelector(`.action--buttons > [href="?view=library&${filter}"]`)
        .classList[active ? 'add' : 'remove']('action--active');
    }
    const visible = new Set();
    for (const mod of await registry.get()) {
      const isEnabled = await registry.isEnabled(mod.id),
        filterConditions =
          (search.has('tag') ? mod.tags.includes(search.get('tag')) : true) &&
          (search.has('enabled') && search.has('disabled')
            ? true
            : search.has('enabled')
            ? isEnabled
            : search.has('disabled')
            ? !isEnabled
            : true);
      if (filterConditions) visible.add(mod.id);
    }
    for (const card of document.querySelectorAll('main > .library--card'))
      card.style.display = 'none';
    for (const card of document.querySelectorAll('main > .library--card'))
      if (visible.has(card.dataset.mod)) card.style.display = '';
    actionButtons.clearFilters();
  }
);

router.addView(
  'mod',
  async () => {
    const mod = (await registry.get()).find((mod) => mod.id === router.getSearch().get('id'));
    if (!mod) return false;
    const $fragment = web.createFragment(web.html`
    <p class="action--buttons">
      <a href="?view=library">
        <span><i data-icon="fa/solid/long-arrow-alt-left"></i></span>
        <span>back to library</span>
      </a>
      <a href="https://github.com/notion-enhancer/extension/tree/main/repo/${encodeURIComponent(
        mod._dir
      )}">
        <span><i data-icon="fa/solid/code"></i></span>
        <span>view source code</span>
      </a>
    </p>`);
    const $card = await components.card(mod);
    $card.querySelector('.library--expand').remove();
    if (mod.options && mod.options.length) {
      const options = web.createElement(web.html`<div class="library--options"></div>`);
      mod.options
        .filter((opt) => !opt.environments || opt.environments.includes(env.name))
        .forEach(async (opt) =>
          options.append(await components.options[opt.type](mod.id, opt))
        );
      $card.append(options);
    }
    $fragment.append(
      $card,
      web.createElement(web.html`
      <article class="documentation--body markdown">
        ${
          (await fs.isFile(`repo/${mod._dir}/README.md`))
            ? fmt.md.render(await fs.getText(`repo/${mod._dir}/README.md`))
            : ''
        }
      </article>`)
    );
    fmt.Prism.highlightAllUnder($fragment);
    actionButtons.reload($fragment);
    return $fragment;
  },
  () => {
    if (document.querySelector('[data-mod]').dataset.mod !== router.getSearch().get('id'))
      router.load(true);
  }
);

router.setDefaultView('library');
router.load();