/// <reference no-default-lib="true" />
/// <reference lib="esnext" />
/// <reference lib="dom" />
/** @jsx h */
/** @jsxFrag Fragment */
import { Fragment, h, render } from "../preact/mod.tsx";
import {
  useCallback,
  useMemo,
  useEffect,
  useState,
} from "../preact/hooks.ts";
import { Asearch, MatchResult } from "../deno-asearch/mod.ts";
import { getMaxDistance } from "./distance.ts";
import { makeSource } from "./makeSource.ts";

export const mount = () => {
  const app = document.createElement("div");
  const shadowRoot = app.attachShadow({ mode: "open" });
  document.body.append(app);
  remove = () => app.remove();
  render(<App />, shadowRoot);
};

let remove: () => void;

const App = () => {
  const [pattern_, setPattern] = useState("");
  const [data, setData] = useState<{ name: string, displayName: string }[]>([]);
  
  useEffect(() => {
    (async () => {
      for await (const candidates of makeSource()) {
        setData((prev) => [...prev, ...candidates]);
      }
    })();
  }, []);
  
  const candidates = useMemo(
    () => {
      const pattern = pattern_.trim().replace(/^\//, "").replace(/\/$/, "");
      if (pattern.length === 0) return [];
      const match = Asearch(`${pattern} `).match;
      
      const maxDistance = getMaxDistance[
        Math.max(...pattern.split(/\s+/).map((word) => word.trim().length))
      ];
      return data.flatMap(
        ({ name, displayName }) => {
          const result1 = match(name, maxDistance);
          const result2 = match(displayName, maxDistance);
          if (!result1.found) {
            if (!result2.found) return [];
            return [{ candidate: displayName, distance: result2.distance }];
          }
          if (!result2.found) return [{ candidate: name, distance: result1.distance }];
          return result1.distance > result2.distance
            ? [{ candidate: displayName, distance: result2.distance }]
            : [{ candidate: name, distance: result1.distance }];
        }
      )
      // 1. 編集距離 2. 文字列超 3. 辞書順序 が小さい順に並び替える
      .sort((a, b) => {
        const diff = a.distance - b.distance;
        if (diff !== 0) return diff;
        const lenDiff = a.candidate.length - b.candidate.length;
        if (lenDiff !== 0) return lenDiff;
        return a.candidate.localeCompare(b.candidate);
      });
    },
    [pattern_, data],
  );

  const handlePattern = useCallback(
    (e: h.JSX.TargetedEvent<HTMLInputElement>) =>
      setPattern(e.currentTarget.value),
    [],
  );

  return (
    <>
      <style>
        {`
      :host {
        position: fixed;
        top: 60px;
        left: 50%;
        transform: translate(-50%, 0);
        padding: 5px;
        border: 1px solid lime;
        border-radius: 5px;
        font-size: 14px;
        background-color: var(--page-bg);
        color: var(--page-text-color);
      }
      input {
        min-width: 40%;
      }
      button {
       position: absolute;
       top: 0px;
       right: 0px;
      }
    `}
      </style>
      <button onClick={remove}>x</button>
      <p>
        <label>
          pattern: <input type="text" value={pattern_} onInput={handlePattern} />
        </label>
      </p>
      <p>
        {candidates.length > 0 ? `Matched ${candidates.length} words` : "No matched"}
        <br />
        <ul>
          {candidates.map(({ candidate }) => (<li key={candidate}>{candidate}</li>))}
        </ul>
      </p>
    </>
  );
};