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

"use strict";

const updateHotkey = (event) => {
    const keys = [];
    for (const modifier of ["metaKey", "ctrlKey", "altKey", "shiftKey"]) {
      if (!event[modifier]) continue;
      const alias = modifier[0].toUpperCase() + modifier.slice(1, -3);
      keys.push(alias);
    }
    // retain keyboard navigation of menu
    if (["Tab", "Escape"].includes(event.key) && !keys.length) {
      return;
    } else event.preventDefault();
    if (!keys.length && ["Backspace", "Delete"].includes(event.key)) {
      event.target.value = "";
    } else if (event.key) {
      let key = event.key;
      if (key === " ") key = "Space";
      if (["+", "="].includes(key)) key = "Plus";
      if (key === "-") key = "Minus";
      if (key === "|") key = "\\";
      if (event.code === "Comma") key = ",";
      if (event.code === "Period") key = ".";
      if (key === "Control") key = "Ctrl";
      // avoid e.g. Shift+Shift, force inclusion of non-modifier
      if (keys.includes(key)) return;
      keys.push(key.length === 1 ? key.toUpperCase() : key);
      event.target.value = keys.join("+");
    }
    event.target.dispatchEvent(new Event("input"));
    event.target.dispatchEvent(new Event("change"));
  },
  updateContrast = ($input, $icon) => {
    $input.style.background = $input.value;
    const [r, g, b, a = 1] = $input.value
      .replace(/^rgba?\(/, "")
      .replace(/\)$/, "")
      .split(",")
      .map((n) => parseFloat(n));
    if (a > 0.5) {
      // pick a contrasting foreground for an rgb background
      // using the percieved brightness constants from http://alienryderflex.com/hsp.html
      const brightness = 0.299 * (r * r) + 0.587 * (g * g) + 0.114 * (b * b);
      $input.style.color = Math.sqrt(brightness) > 165.75 ? "#000" : "#fff";
    } else $input.style.color = "#000";
    $icon.style.color = $input.style.color;
    $icon.style.opacity = "0.7";
  },
  readUpload = async (event) => {
    const file = event.target.files[0],
      reader = new FileReader();
    return new Promise((res) => {
      reader.onload = async (progress) => {
        const content = progress.currentTarget.result,
          upload = { filename: file.name, content };
        res(upload);
      };
      reader.readAsText(file);
    });
  };

function Input({
  type,
  icon,
  variant,
  extensions,
  class: className,
  _get,
  _set,
  _requireReload = true,
  ...props
}) {
  let $filename, $clear;
  const { html, extendProps, setState, useState } = globalThis.__enhancerApi;
  Coloris({ format: "rgb" });

  type ??= "text";
  if (type === "text") icon ??= "text-cursor";
  if (type === "number") icon ??= "hash";
  if (type === "hotkey") icon ??= "command";
  if (type === "color") icon ??= "pipette";

  if (type === "file") {
    icon ??= "file-up";
    $filename = html`<span class="ml-[6px]">Upload a file</span>`;
    $clear = html`<button
      style="display: none"
      class="h-[14px] transition duration-[20ms]
      flex text-[color:var(--theme--fg-secondary)]
      hover:text-[color:var(--theme--fg-primary)]"
      onclick=${() => _set?.({ filename: "", content: "" })}
    >
      <i class="i-x size-[14px]"></i>
    </button>`;
    props.accept = extensions
      ?.map((ext) => (ext.startsWith(".") ? ext : `.${ext}`))
      .join(",");
  }

  const $input = html`<input
      type=${["hotkey", "color"].includes(type) ? "text" : type}
      class="h-full w-full pb-px text-[14px] leading-[1.2]
      ${variant === "lg" ? "pl-[12px] pr-[40px]" : "pl-[8px] pr-[32px]"}
      appearance-none bg-transparent ${type === "file" ? "hidden" : ""}
      ${type === "hotkey" ? "text-[color:var(--theme--fg-secondary)]" : ""}
      ${type === "color"
        ? "font-medium"
        : "border-(& [color:var(--theme--fg-border)])"}"
      data-coloris=${type === "color"}
      ...${props}
    />`,
    $icon = html`<span
      class="${variant === "lg" ? "pr-[12px]" : "pr-[8px]"}
      absolute flex items-center h-full pointer-events-none
      text-[color:var(--theme--fg-secondary)] right-0 top-0"
      ><i class="i-${icon} size-[16px]"></i>
    </span>`;

  let _initialValue;
  extendProps($input, {
    onclick: () => {
      // change text to "uploading..." until file has uploaded
      // to reassure users experiencing latency while file is processed
      if (type === "file") $filename.innerText = "Uploading...";
    },
    onchange: (event) => {
      if (_set && type === "file") {
        readUpload(event)
          .then(_set)
          // refocus iframe after file has uploaded,
          // sometimes switching back after opening the
          // native file upload menu causes a loss of focus
          .then(() => window.focus());
      } else _set?.($input.value);
    },
    onrerender: async () => {
      const value = (await _get?.()) ?? $input.value ?? "";
      if (type === "file") {
        $filename.innerText = value?.filename || "Upload a file";
        $clear.style.display = value?.filename ? "" : "none";
        if (_requireReload) {
          _initialValue ??= value?.content || "";
          if ((value?.content || "") !== _initialValue) {
            setState({ databaseUpdated: true });
          }
        }
      } else {
        if ($input.value !== value) $input.value = value;
        if (_requireReload) {
          _initialValue ??= value;
          if (value !== _initialValue) setState({ databaseUpdated: true });
        }
        if (type === "color") updateContrast($input, $icon);
      }
    },
    onkeydown: type === "hotkey" ? updateHotkey : undefined,
    oninput: type === "color" ? () => _set?.($input.value) : undefined,
  });
  useState(["rerender"], () => $input.onrerender?.());

  return type === "file"
    ? html`<div
        class="notion-enhancer--menu-file-input shrink-0
        flex items-center gap-[8px] ${className ?? ""}"
      >
        <label
          tabindex="0"
          class="flex items-center cursor-pointer select-none
          px-[8px] bg-[color:var(--theme--bg-secondary)]
          h-[28px] rounded-[4px] transition duration-[20ms]
          text-([14px] [color:var(--theme--fg-secondary)])
          border-(& [color:var(--theme--fg-border)])
          hover:bg-[color:var(--theme--bg-hover)]"
          onkeydown=${(event) => {
            if ([" ", "Enter"].includes(event.key)) {
              event.preventDefault();
              $input.click();
            }
          }}
          >${$input}${$icon.children[0]}${$filename}
        </label>
        ${$clear}
      </div>`
    : html`<label
        class="notion-enhancer--menu-input 
        ${variant === "lg" ? "h-[32px]" : "h-[28px]"}
        relative overflow-hidden rounded-[4px] w-full inline-block
        focus-within:ring-(& [color:var(--theme--accent-primary)])
        ${className ?? ""} ${type === "color"
          ? "bg-([image:repeating-linear-gradient(45deg,#aaa_25%,transparent_25%,transparent_75%,#aaa_75%,#aaa),repeating-linear-gradient(45deg,#aaa_25%,#fff_25%,#fff_75%,#aaa_75%,#aaa)] [position:0_0,4px_4px] [size:8px_8px])"
          : "bg-[color:var(--theme--bg-hover)]"}"
        >${$input}${$icon}
      </label>`;
}

export { Input };
