<script lang="ts">
  import browser from "webextension-polyfill";
  import { UpEndApi } from "upend/api";
  import { cleanInstanceUrl, instanceUrlStore } from "./common";
  import { onMount } from "svelte";
  import "./main.scss";

  const api = new UpEndApi("http://localhost:8093");

  let visitingUpEnd = false;

  let opening = false;
  let openError: string | undefined;

  let instanceUrl: string;
  $: instanceUrl = $instanceUrlStore;
  let instanceUrlModified = false;
  $: instanceUrlModified = $instanceUrlStore !== instanceUrl;

  let instanceVersion: string;
  let instanceVersionError: string;
  $: Boolean($instanceUrlStore) && fetchVersion();

  async function fetchVersion() {
    instanceVersion = undefined;
    instanceVersionError = undefined;

    try {
      const vaultInfo = await api.fetchInfo();
      instanceVersion = vaultInfo.version;
    } catch (err: unknown) {
      instanceVersionError = processError(err);
    }
  }

  async function getCurrentUrl() {
    const currentTab = (
      await browser.tabs.query({
        active: true,
        currentWindow: true,
      })
    )[0];
    return currentTab.url;
  }

  let currentUrl: string | undefined;
  let contentType: string | undefined;
  onMount(async () => {
    visitingUpEnd =
      (
        await browser.tabs.executeScript(undefined, {
          code: `document.querySelector('meta[name="application-name"]')?.content`,
        })
      )[0] === "UpEnd";

    currentUrl = await getCurrentUrl();

    browser.tabs.onUpdated.addListener(async () => {
      if (currentUrl !== (await getCurrentUrl())) {
        window.close();
      }
    });

    contentType = (
      await browser.tabs.executeScript(undefined, {
        code: "document.contentType",
      })
    )[0];
  });

  enum PRIMARY_TYPE {
    URL = "URL",
    CONTENT = "Content",
    UNKNOWN = "???",
  }

  let primaryType = PRIMARY_TYPE.UNKNOWN;
  $: primaryType = !contentType
    ? PRIMARY_TYPE.UNKNOWN
    : contentType == "text/html"
    ? PRIMARY_TYPE.URL
    : PRIMARY_TYPE.CONTENT;

  enum PRIMARY_ACTION {
    OPEN = "Open",
    SAVE = "Save",
    UNKNOWN = "???",
  }

  let primaryAction = PRIMARY_ACTION.UNKNOWN;
  $: {
    if (primaryType !== PRIMARY_TYPE.UNKNOWN) {
      let count =
        primaryType == PRIMARY_TYPE.URL ? urlEntriesCount : contentEntriesCount;
      if (count !== undefined) {
        primaryAction = count > 0 ? PRIMARY_ACTION.OPEN : PRIMARY_ACTION.SAVE;
      }
    }
  }

  let urlAddress: string | undefined = undefined;
  let contentAddress: string | undefined = undefined;

  $: currentUrl &&
    api
      .getAddress({ url: currentUrl })
      .then((address) => {
        urlAddress = address;
      })
      .catch((err) => {
        console.error(err);
        urlAddress = undefined;
      });

  $: currentUrl &&
    api
      .getAddress({ urlContent: currentUrl })
      .then((address) => (contentAddress = address))
      .catch((err) => {
        console.error(err);
        contentAddress = undefined;
      });

  let urlEntriesCount: number | undefined = undefined;
  let contentEntriesCount: number | undefined = undefined;

  let urlAdded: Date | undefined;
  let contentAdded: Date | undefined;

  $: urlAddress &&
    api
      .fetchEntity(urlAddress)
      .then((obj) => {
        urlEntriesCount = Object.keys(obj.listing.entries).length;
        let added = obj.get("ADDED");
        urlAdded =
          typeof added === "number" ? new Date(added * 1000) : undefined;
      })
      .catch((err) => {
        console.error(err);
        urlEntriesCount = undefined;
        urlAdded = undefined;
      });

  $: contentAddress &&
    api
      .fetchEntity(contentAddress)
      .then((obj) => {
        contentEntriesCount = Object.keys(obj.listing.entries).length;
        let added = obj.get("ADDED");
        contentAdded =
          typeof added === "number" ? new Date(added * 1000) : undefined;
      })
      .catch((err) => {
        console.error(err);
        contentEntriesCount = undefined;
      });

  async function performPrimary() {
    if (
      primaryType == PRIMARY_TYPE.UNKNOWN ||
      primaryAction == PRIMARY_ACTION.UNKNOWN
    ) {
      return;
    }

    switch (primaryType) {
      case PRIMARY_TYPE.URL:
        await performUrl();
        break;
      case PRIMARY_TYPE.CONTENT:
        await performContent();
        break;
    }
  }

  async function performUrl() {
    if (urlEntriesCount === undefined) {
      return;
    }

    if (urlEntriesCount === 0) {
      await api.putEntry({
        entity: {
          t: "Url",
          c: currentUrl,
        },
      });
    }

    visitAddress(urlAddress);
  }

  async function performContent() {
    if (contentEntriesCount === undefined) {
      return;
    }

    if (contentEntriesCount === 0) {
      await api.putBlob(new URL(currentUrl));
    }

    visitAddress(contentAddress);
  }

  function visitAddress(address: string) {
    browser.tabs.create({ url: `${$cleanInstanceUrl}/#/browse/${address}` });
    window.close();
  }

  function processError(err: unknown): string {
    if (err instanceof Error) {
      if (err.message.includes("NetworkError")) {
        return "Network Error. Is UpEnd running?";
      } else {
        return err.message;
      }
    } else {
      return String(err);
    }
  }
</script>

<main>
  {#if !visitingUpEnd}
    <div class="primary-controls">
      <button
        class="button"
        disabled={primaryAction == PRIMARY_ACTION.UNKNOWN}
        on:click={performPrimary}
      >
        {primaryAction} as {primaryType}
      </button>
      <div class="sublabel">Content type: {contentType || "???"}</div>
    </div>
    <div class="controls row">
      <div class="labeled-button">
        <button
          class="button"
          disabled={urlEntriesCount === undefined}
          on:click={performUrl}
        >
          {urlEntriesCount === undefined
            ? "???"
            : urlEntriesCount > 0
            ? "Open"
            : "Save"} URL
        </button>
        <div class="sublabel">
          {#if urlAdded}
            Added at {urlAdded.toLocaleString()}
          {:else}
            ---
          {/if}
        </div>
      </div>
      <div class="labeled-button">
        <button
          class="button"
          disabled={contentEntriesCount === undefined}
          on:click={performContent}
        >
          {contentEntriesCount === undefined
            ? "???"
            : contentEntriesCount > 0
            ? "Open"
            : "Save"} Content
        </button>
        <div class="sublabel">
          {#if contentAdded}
            Added at {contentAdded.toLocaleString()}
          {:else}
            ---
          {/if}
        </div>
      </div>
    </div>
    {#if opening && !openError}
      <div class="status-label">Working, please wait...</div>
    {/if}
    {#if openError}
      <div class="status-label error">{openError}</div>
    {/if}
    <hr />
    <div class="row">
      <label>
        Instance URL
        <input
          class="instance-input"
          type="url"
          bind:value={instanceUrl}
          class:modified={instanceUrlModified}
        />
      </label>
      <button class="button" on:click={() => ($instanceUrlStore = instanceUrl)}>
        Save
      </button>
    </div>
    <div class="version">
      Status: {#if !instanceVersionError}
        {`OK, v.${instanceVersion}` || "???"}
      {:else}
        <div class="error">{instanceVersionError}</div>
      {/if}
    </div>
  {:else}
    <div class="visitingUpEnd">You rock :)</div>
  {/if}
</main>

<style lang="scss">
  @use "../../webui/src/styles/colors";

  main {
    padding: 1em;
  }

  input {
    background: var(--background);
    color: var(--foreground);
    border: 1px solid var(--foreground);
    border-radius: 2px;
  }

  input[type="url"] {
    font-family: var(--monospace-font);

    &:focus-visible {
      outline: 1px solid var(--primary-lighter);
    }

    &:invalid {
      color: colors.$red;
      outline: 2px solid colors.$red;
    }
  }

  .instance-input.modified {
    color: colors.$yellow;
  }

  hr {
    margin: 1rem 0;
  }

  .controls {
    display: flex;
    justify-content: space-evenly;
  }

  .primary-controls {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.25rem;

    margin-bottom: 1rem;
    .button {
      font-size: 1.5rem;
    }
  }

  .row {
    display: flex;
    align-items: center;
    gap: 1rem;
  }

  .labeled-button {
    display: flex;
    flex-direction: column;
    align-items: center;
  }

  .status-label {
    margin-top: 1rem;
    text-align: center;
  }

  .sublabel {
    font-size: 0.75rem;
  }

  .error {
    color: colors.$red;
  }

  .version .error {
    display: inline;
  }
</style>
