"use client";

import { useEffect, useRef, useState } from "react";
import {
  Bold, Italic, Heading2, Quote, List, ListOrdered, Link2,
  Image as ImageIcon, Loader2,
} from "lucide-react";
import { shrinkImage } from "@/lib/image";
import { uploadFile } from "@/lib/upload";

/**
 * A small rich text editor.
 *
 * Bold, italic, a heading, a quote, lists, links and photos — the things
 * an article actually needs. It hands back HTML, which the server
 * sanitises before it is stored: nothing here is trusted on the way in.
 */
export function RichText({
  value,
  onChange,
  placeholder = "Write your article…",
}: {
  value: string;
  onChange: (html: string) => void;
  placeholder?: string;
}) {
  const ref = useRef<HTMLDivElement>(null);
  const fileRef = useRef<HTMLInputElement>(null);
  const [uploading, setUploading] = useState(false);

  // Only on mount: writing back on every change would move the caret to
  // the end of the text on every keystroke.
  useEffect(() => {
    if (ref.current && value && ref.current.innerHTML !== value) {
      ref.current.innerHTML = value;
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const push = () => onChange(ref.current?.innerHTML ?? "");

  function run(command: string, argument?: string) {
    ref.current?.focus();
    // Deprecated, and still the only thing every browser agrees on for a
    // contenteditable this size.
    document.execCommand(command, false, argument);
    push();
  }

  function addLink() {
    const url = window.prompt("Link to where?");
    if (!url) return;
    if (!/^https?:\/\//i.test(url) && !url.startsWith("/")) {
      run("createLink", `https://${url}`);
      return;
    }
    run("createLink", url);
  }

  async function addPhoto(e: React.ChangeEvent<HTMLInputElement>) {
    const file = e.target.files?.[0];
    e.target.value = "";
    if (!file) return;

    setUploading(true);
    const smaller = await shrinkImage(file, 1600).catch(() => file);
    const res = await uploadFile(smaller);
    setUploading(false);

    if ("error" in res) {
      window.alert(res.error);
      return;
    }
    run("insertHTML", `<img src="${res.url}" alt="" />`);
  }

  const TOOLS = [
    { icon: Bold, label: "Bold", run: () => run("bold") },
    { icon: Italic, label: "Italic", run: () => run("italic") },
    { icon: Heading2, label: "Heading", run: () => run("formatBlock", "<h2>") },
    { icon: Quote, label: "Quote", run: () => run("formatBlock", "<blockquote>") },
    { icon: List, label: "Bullets", run: () => run("insertUnorderedList") },
    { icon: ListOrdered, label: "Numbers", run: () => run("insertOrderedList") },
    { icon: Link2, label: "Link", run: addLink },
  ];

  return (
    <div className="rt">
      <div className="rt-bar">
        {TOOLS.map((t) => (
          <button
            key={t.label}
            type="button"
            title={t.label}
            onMouseDown={(e) => e.preventDefault()}
            onClick={t.run}
            className="rt-btn"
          >
            <t.icon size={16} />
          </button>
        ))}

        <span className="rt-sep" />

        <button
          type="button"
          title="Add a photo"
          onClick={() => fileRef.current?.click()}
          className="rt-btn"
          disabled={uploading}
        >
          {uploading ? <Loader2 size={16} className="animate-spin" /> : <ImageIcon size={16} />}
        </button>
        <input
          ref={fileRef}
          type="file"
          accept="image/*"
          hidden
          onChange={addPhoto}
        />
      </div>

      <div
        ref={ref}
        contentEditable
        suppressContentEditableWarning
        onInput={push}
        onBlur={push}
        data-placeholder={placeholder}
        className="rt-area"
      />
    </div>
  );
}
