/**
 * PostEditor — Tiptap rich text editor for creator posts with file attachments.
 *
 * /creator/posts/new  → create mode
 * /creator/posts/:postId/edit → edit mode (loads existing post)
 */

import { useCallback, useEffect, useRef, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { useEditor, EditorContent, type Editor } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import Link from "@tiptap/extension-link";
import Placeholder from "@tiptap/extension-placeholder";
import { useDocumentTitle } from "../hooks/useDocumentTitle";
import {
  createPost,
  getPost,
  updatePost,
  uploadFile,
  type PostAttachmentRead,
  type PostRead,
} from "../api/posts";
import { ApiError } from "../api/client";
import { SidebarNav } from "./CreatorDashboard";
import styles from "./PostEditor.module.css";

// ── Toolbar Button ──────────────────────────────────────────────────────────

function ToolbarBtn({
  label,
  active,
  onClick,
  children,
}: {
  label: string;
  active?: boolean;
  onClick: () => void;
  children: React.ReactNode;
}) {
  return (
    <button
      type="button"
      className={`${styles.toolbarBtn}${active ? ` ${styles.toolbarBtnActive}` : ""}`}
      onClick={onClick}
      aria-label={label}
      title={label}
    >
      {children}
    </button>
  );
}

// ── Editor Toolbar ──────────────────────────────────────────────────────────

function EditorToolbar({ editor }: { editor: Editor | null }) {
  if (!editor) return null;

  const setLink = () => {
    const prev = editor.getAttributes("link").href ?? "";
    const url = window.prompt("URL", prev);
    if (url === null) return; // cancelled
    if (url === "") {
      editor.chain().focus().extendMarkRange("link").unsetLink().run();
    } else {
      editor.chain().focus().extendMarkRange("link").setLink({ href: url }).run();
    }
  };

  return (
    <div className={styles.toolbar} role="toolbar" aria-label="Text formatting">
      <ToolbarBtn label="Bold" active={editor.isActive("bold")} onClick={() => editor.chain().focus().toggleBold().run()}>
        <strong>B</strong>
      </ToolbarBtn>
      <ToolbarBtn label="Italic" active={editor.isActive("italic")} onClick={() => editor.chain().focus().toggleItalic().run()}>
        <em>I</em>
      </ToolbarBtn>
      <span className={styles.toolbarSep} />
      <ToolbarBtn label="Heading 2" active={editor.isActive("heading", { level: 2 })} onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}>
        H2
      </ToolbarBtn>
      <ToolbarBtn label="Heading 3" active={editor.isActive("heading", { level: 3 })} onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()}>
        H3
      </ToolbarBtn>
      <span className={styles.toolbarSep} />
      <ToolbarBtn label="Bullet list" active={editor.isActive("bulletList")} onClick={() => editor.chain().focus().toggleBulletList().run()}>
        •
      </ToolbarBtn>
      <ToolbarBtn label="Ordered list" active={editor.isActive("orderedList")} onClick={() => editor.chain().focus().toggleOrderedList().run()}>
        1.
      </ToolbarBtn>
      <span className={styles.toolbarSep} />
      <ToolbarBtn label="Code block" active={editor.isActive("codeBlock")} onClick={() => editor.chain().focus().toggleCodeBlock().run()}>
        {"</>"}
      </ToolbarBtn>
      <ToolbarBtn label="Link" active={editor.isActive("link")} onClick={setLink}>
        🔗
      </ToolbarBtn>
    </div>
  );
}

// ── File size formatter ─────────────────────────────────────────────────────

function formatBytes(bytes: number): string {
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}

// ── Main Component ──────────────────────────────────────────────────────────

export default function PostEditor() {
  const { postId } = useParams<{ postId: string }>();
  const isEdit = Boolean(postId);
  useDocumentTitle(isEdit ? "Edit Post" : "New Post");

  const navigate = useNavigate();

  // Form state
  const [title, setTitle] = useState("");
  const [isPublished, setIsPublished] = useState(false);
  const [existingAttachments, setExistingAttachments] = useState<PostAttachmentRead[]>([]);
  const [pendingFiles, setPendingFiles] = useState<File[]>([]);
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [loading, setLoading] = useState(isEdit);
  const fileInputRef = useRef<HTMLInputElement>(null);

  // Tiptap editor
  const editor = useEditor({
    immediatelyRender: false,
    extensions: [
      StarterKit.configure({
        heading: { levels: [2, 3] },
      }),
      Link.configure({
        openOnClick: false,
        HTMLAttributes: { class: styles.editorLink },
      }),
      Placeholder.configure({
        placeholder: "Write your post…",
      }),
    ],
    content: "",
    editorProps: {
      attributes: {
        class: styles.proseMirror ?? "",
      },
    },
  });

  // Load existing post in edit mode
  useEffect(() => {
    if (!isEdit || !postId) return;
    let cancelled = false;

    (async () => {
      try {
        const post: PostRead = await getPost(postId);
        if (cancelled) return;
        setTitle(post.title);
        setIsPublished(post.is_published);
        setExistingAttachments(post.attachments);
        editor?.commands.setContent(post.body_json);
      } catch (err) {
        if (!cancelled) {
          setError(err instanceof ApiError ? err.detail : "Failed to load post");
        }
      } finally {
        if (!cancelled) setLoading(false);
      }
    })();

    return () => { cancelled = true; };
  }, [postId, isEdit, editor]);

  // File handlers
  const addFiles = useCallback((files: FileList | File[]) => {
    setPendingFiles((prev) => [...prev, ...Array.from(files)]);
  }, []);

  const removePendingFile = useCallback((idx: number) => {
    setPendingFiles((prev) => prev.filter((_, i) => i !== idx));
  }, []);

  // Drag-and-drop
  const [dragOver, setDragOver] = useState(false);

  const handleDrop = useCallback(
    (e: React.DragEvent) => {
      e.preventDefault();
      setDragOver(false);
      if (e.dataTransfer.files.length) addFiles(e.dataTransfer.files);
    },
    [addFiles],
  );

  // Save handler
  const handleSave = async () => {
    if (!editor) return;
    if (!title.trim()) {
      setError("Title is required");
      return;
    }

    setSaving(true);
    setError(null);

    try {
      const bodyJson = editor.getJSON();

      let post: PostRead;
      if (isEdit && postId) {
        post = await updatePost(postId, {
          title: title.trim(),
          body_json: bodyJson,
          is_published: isPublished,
        });
      } else {
        post = await createPost({
          title: title.trim(),
          body_json: bodyJson,
          is_published: isPublished,
        });
      }

      // Upload any pending files
      for (const file of pendingFiles) {
        await uploadFile(post.id, file);
      }

      navigate("/creator/dashboard");
    } catch (err) {
      setError(err instanceof ApiError ? err.detail : "Failed to save post");
    } finally {
      setSaving(false);
    }
  };

  if (loading) {
    return (
      <div className={styles.layout}>
        <SidebarNav />
        <div className={styles.content}>
          <p className={styles.loadingText}>Loading post…</p>
        </div>
      </div>
    );
  }

  return (
    <div className={styles.layout}>
      <SidebarNav />
      <div className={styles.content}>
        <div className={styles.header}>
          <h1 className={styles.pageTitle}>{isEdit ? "Edit Post" : "New Post"}</h1>
        </div>

        {error && (
          <div className={styles.errorBanner} role="alert">
            {error}
          </div>
        )}

        {/* Title */}
        <input
          type="text"
          className={styles.titleInput}
          value={title}
          onChange={(e) => setTitle(e.target.value)}
          placeholder="Post title"
          maxLength={500}
          autoFocus
        />

        {/* Editor */}
        <div className={styles.editorWrapper}>
          <EditorToolbar editor={editor} />
          <EditorContent editor={editor} className={styles.editorContent} />
        </div>

        {/* File Attachments */}
        <div className={styles.attachmentsSection}>
          <h3 className={styles.attachmentsTitle}>File Attachments</h3>

          {/* Existing attachments (edit mode) */}
          {existingAttachments.length > 0 && (
            <ul className={styles.fileList}>
              {existingAttachments.map((att) => (
                <li key={att.id} className={styles.fileItem}>
                  <span className={styles.fileIcon}>📎</span>
                  <span className={styles.fileName}>{att.filename}</span>
                  <span className={styles.fileSize}>{formatBytes(att.size_bytes)}</span>
                </li>
              ))}
            </ul>
          )}

          {/* Pending files to upload */}
          {pendingFiles.length > 0 && (
            <ul className={styles.fileList}>
              {pendingFiles.map((file, idx) => (
                <li key={`pending-${idx}`} className={styles.fileItem}>
                  <span className={styles.fileIcon}>📄</span>
                  <span className={styles.fileName}>{file.name}</span>
                  <span className={styles.fileSize}>{formatBytes(file.size)}</span>
                  <button
                    type="button"
                    className={styles.fileRemoveBtn}
                    onClick={() => removePendingFile(idx)}
                    aria-label={`Remove ${file.name}`}
                  >
                    ✕
                  </button>
                </li>
              ))}
            </ul>
          )}

          {/* Drop zone */}
          <div
            className={`${styles.dropZone}${dragOver ? ` ${styles.dropZoneActive}` : ""}`}
            onDragOver={(e) => { e.preventDefault(); setDragOver(true); }}
            onDragLeave={() => setDragOver(false)}
            onDrop={handleDrop}
            onClick={() => fileInputRef.current?.click()}
            role="button"
            tabIndex={0}
            onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") fileInputRef.current?.click(); }}
          >
            <p className={styles.dropZoneText}>
              {dragOver ? "Drop files here" : "Drag & drop files or click to browse"}
            </p>
            <p className={styles.dropZoneHint}>Presets, sample packs, project files, etc.</p>
          </div>
          <input
            ref={fileInputRef}
            type="file"
            multiple
            className={styles.hiddenFileInput}
            onChange={(e) => { if (e.target.files?.length) addFiles(e.target.files); e.target.value = ""; }}
          />
        </div>

        {/* Footer controls */}
        <div className={styles.footer}>
          <label className={styles.publishToggle}>
            <input
              type="checkbox"
              checked={isPublished}
              onChange={(e) => setIsPublished(e.target.checked)}
            />
            <span>Publish</span>
          </label>
          <div className={styles.footerActions}>
            <button
              type="button"
              className={styles.cancelBtn}
              onClick={() => navigate("/creator/dashboard")}
            >
              Cancel
            </button>
            <button
              type="button"
              className={styles.saveBtn}
              onClick={handleSave}
              disabled={saving}
            >
              {saving ? "Saving…" : isEdit ? "Update Post" : "Create Post"}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}
