/**
 * JournalInput — chat-style textarea with command dispatch and autocomplete.
 *
 * - Enter sends, Shift+Enter inserts newline
 * - Plain text → "narrative" type
 * - `/roll 3d6kh1` → "roll.request" type
 * - `/link path#section` → "article.reveal" type
 * - `/` alone opens completions dropdown (populated from /__COMPLETIONS.json)
 */

import { Component, createSignal, onMount, onCleanup, Show, For } from "solid-js";
import { sendMessage, revertLatest, canRevert, useJournalStream } from "../stores/journalStream";
import { useJournalCompletions, type DiceCompletion, type LinkCompletion } from "./completions";

interface CompletionItem {
  label: string;
  /** "command" | "value" — used for styling and insertion */
  kind: "command" | "value";
  insertText: string;
}

interface ParsedInput {
  type: "narrative" | "roll.request" | "article.reveal";
  payload: Record<string, unknown>;
  error?: string;
}

export const JournalInput: Component = () => {
  const stream = useJournalStream();
  const completions = useJournalCompletions();

  const [text, setText] = createSignal("");
  const [error, setError] = createSignal<string | null>(null);
  const [sending, setSending] = createSignal(false);
  const [showCompletions, setShowCompletions] = createSignal(false);
  const [selectedIdx, setSelectedIdx] = createSignal(0);

  let textareaRef!: HTMLTextAreaElement;
  let completionsRef!: HTMLDivElement;

  // ---------- Parsing ----------

  function parseInput(raw: string): ParsedInput {
    if (raw.startsWith("/roll ")) {
      const notation = raw.slice("/roll ".length).trim();
      if (!notation) return { type: "roll.request", payload: {}, error: "Dice notation required" };
      return { type: "roll.request", payload: { notation, label: notation } };
    }

    if (raw.startsWith("/link ")) {
      const arg = raw.slice("/link ".length).trim();
      if (!arg) return { type: "article.reveal", payload: {}, error: "Path required" };
      const [path, section] = parseLinkArg(arg);
      return { type: "article.reveal", payload: { path, section: section ?? undefined } };
    }

    // /roll or /link with no space — show completions, don't send
    if (raw === "/roll" || raw === "/link") {
      return { type: "narrative", payload: {}, error: "Complete the command" };
    }

    return { type: "narrative", payload: { text: raw } };
  }

  /** Parse "path#section" or just "path" */
  function parseLinkArg(arg: string): [string, string | null] {
    const hashIdx = arg.indexOf("#");
    if (hashIdx === -1) return [arg, null];
    return [arg.slice(0, hashIdx), arg.slice(hashIdx + 1) || null];
  }

  // ---------- Send ----------

  function handleSend() {
    const raw = text().trim();
    if (!raw) return;

    const parsed = parseInput(raw);
    if (parsed.error) {
      setError(parsed.error);
      return;
    }

    setError(null);
    setSending(true);

    const result = sendMessage(parsed.type, parsed.payload);
    if (!result.success) {
      setError(result.error);
    } else {
      setText("");
    }

    setSending(false);
    textareaRef?.focus();
  }

  function handleRevert() {
    revertLatest();
  }

  // ---------- Completions ----------

  function buildCompletions(): CompletionItem[] {
    const raw = text().trim();

    // Show commands when user types /
    if (raw === "" || raw === "/") {
      return [
        { label: "/roll", kind: "command", insertText: "/roll " },
        { label: "/link", kind: "command", insertText: "/link " },
      ];
    }

    // After /roll — show dice suggestions
    if (raw.startsWith("/roll ")) {
      const prefix = raw.slice("/roll ".length).toLowerCase();
      return completions.dice
        .filter(
          (d) =>
            d.notation.toLowerCase().includes(prefix) ||
            d.label.toLowerCase().includes(prefix),
        )
        .slice(0, 8)
        .map((d) => ({
          label: d.notation,
          kind: "value" as const,
          insertText: "/roll " + d.notation,
        }));
    }

    // After /link — show article and heading suggestions
    if (raw.startsWith("/link ")) {
      const prefix = raw.slice("/link ".length).toLowerCase();
      return completions.links
        .filter(
          (l) =>
            l.path.toLowerCase().includes(prefix) ||
            l.label.toLowerCase().includes(prefix),
        )
        .slice(0, 8)
        .map((l) => {
          const insert = l.section ? `/link ${l.path}#${l.section}` : `/link ${l.path}`;
          return {
            label: l.label,
            kind: "value" as const,
            insertText: insert,
          };
        });
    }

    return [];
  }

  function currentCompletions(): CompletionItem[] {
    return buildCompletions();
  }

  function acceptCompletion(item: CompletionItem) {
    setText(item.insertText);
    setShowCompletions(false);
    textareaRef?.focus();
  }

  function selectCompletion(dir: "up" | "down") {
    const comps = currentCompletions();
    if (comps.length === 0) return;
    setSelectedIdx((prev) => {
      if (dir === "down") return (prev + 1) % comps.length;
      return (prev - 1 + comps.length) % comps.length;
    });
  }

  // ---------- Keyboard ----------

  function handleKeyDown(e: KeyboardEvent) {
    const comps = currentCompletions();
    const open = showCompletions();

    // Completions: navigate & accept
    if (open && comps.length > 0) {
      if (e.key === "ArrowDown") { e.preventDefault(); selectCompletion("down"); return; }
      if (e.key === "ArrowUp") { e.preventDefault(); selectCompletion("up"); return; }
      if (e.key === "Tab" || e.key === "Enter") {
        e.preventDefault();
        acceptCompletion(comps[selectedIdx()]);
        return;
      }
      if (e.key === "Escape") { setShowCompletions(false); return; }
    }

    // Send
    if (e.key === "Enter" && !e.shiftKey) {
      e.preventDefault();
      handleSend();
    }
  }

  function handleInput() {
    const raw = text();

    // Show completions when user types / at the start
    if (raw === "/" || raw.startsWith("/roll ") || raw.startsWith("/link ")) {
      setShowCompletions(true);
      setSelectedIdx(0);
    } else {
      setShowCompletions(false);
    }

    // Auto-resize
    textareaRef.style.height = "auto";
    textareaRef.style.height = Math.min(textareaRef.scrollHeight, 150) + "px";
  }

  // ---------- Click outside ----------

  onMount(() => {
    const handler = (e: MouseEvent) => {
      if (completionsRef && !completionsRef.contains(e.target as Node)) {
        setShowCompletions(false);
      }
    };
    document.addEventListener("mousedown", handler);
    onCleanup(() => document.removeEventListener("mousedown", handler));
  });

  const showRevert = () => canRevert() && stream.myName === "gm";

  return (
    <div class="border-t border-gray-200 bg-white relative">
      {/* Completions dropdown — positioned above the textarea */}
      <Show when={showCompletions() && currentCompletions().length > 0}>
        <div
          ref={completionsRef}
          class="absolute bottom-full left-0 right-0 bg-white border border-gray-200 rounded-t shadow-lg mb-0.5 max-h-40 overflow-y-auto z-50"
        >
          <For each={currentCompletions()}>
            {(item, idx) => (
              <div
                class={`flex items-center gap-2 px-3 py-1.5 cursor-pointer text-sm ${
                  idx() === selectedIdx()
                    ? "bg-blue-50 text-blue-700"
                    : "hover:bg-gray-100 text-gray-700"
                }`}
                onClick={() => acceptCompletion(item)}
                onMouseEnter={() => setSelectedIdx(idx())}
              >
                <span
                  class={`text-xs px-1 rounded shrink-0 ${
                    item.kind === "command"
                      ? "bg-purple-100 text-purple-700"
                      : "bg-gray-100 text-gray-500"
                  }`}
                >
                  {item.kind === "command" ? "cmd" : "val"}
                </span>
                <span class="font-mono truncate flex-1">{item.label}</span>
              </div>
            )}
          </For>
        </div>
      </Show>

      {/* Textarea + actions */}
      <div class="flex flex-col">
        <textarea
          ref={textareaRef}
          value={text()}
          onInput={handleInput}
          onKeyDown={handleKeyDown}
          placeholder="Type a message, or /roll or /link..."
          rows={2}
          class="w-full resize-none border-0 px-3 pt-2.5 pb-1 text-sm
                 text-gray-800 placeholder-gray-400 focus:outline-none
                 bg-transparent min-h-[60px]"
        />

        {/* Bottom row: error + buttons */}
        <div class="flex items-center justify-between px-2 pb-2">
          <Show when={error()}>
            <p class="text-red-500 text-xs truncate max-w-[60%]">{error()}</p>
          </Show>
          <div class="flex items-center gap-1 ml-auto">
            <Show when={showRevert}>
              <button
                onClick={handleRevert}
                class="text-xs text-gray-400 hover:text-red-500 px-1.5 py-0.5"
                title="Revert last message"
              >
                ↩
              </button>
            </Show>
            <button
              onClick={handleSend}
              disabled={sending() || !text().trim()}
              class="bg-blue-600 text-white rounded px-3 py-1 text-xs
                     hover:bg-blue-700 disabled:opacity-40 disabled:cursor-not-allowed
                     transition-colors"
            >
              Send
            </button>
          </div>
        </div>
      </div>
    </div>
  );
};
