"use client";

import { useState, useEffect, useRef } from "react";
import {
  BookOpen,
  ChevronLeft,
  ChevronRight,
  Calendar,
  FileText,
  Download,
  Settings2,
  Video,
  Flame,
  PenLine,
  Check,
  Lock,
  Pencil,
} from "lucide-react";
import { RemindMeButton } from "@/components/reminders/remind-me-button";
import { ReviewShareStudio } from "@/components/reviews/review-share-studio";
import { ShareCardButton } from "@/components/shared/mindavio-share-card";
import { Button } from "@/components/ui/button";
import { Modal } from "@/components/ui/modal";
import { Skeleton } from "@/components/ui/skeleton";
import { cn } from "@/lib/utils";
import { useJournal } from "@/hooks/use-journal";
import type { JournalEntry, Mood } from "@/types";
import {
  BackgroundPicker,
  BackgroundVideoLayer,
  MindavioLogo,
  useBackgroundPicker,
  BG_PRESETS,
} from "@/components/shared/background-picker";
import { captureElementWithVideoBackground } from "@/lib/review-video-export";

import { useEditor, EditorContent } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import Placeholder from "@tiptap/extension-placeholder";

const MOODS: { value: Mood; emoji: string; label: string; color: string }[] = [
  { value: "great",    emoji: "🤩", label: "Super",     color: "text-amber-300  bg-amber-500/[0.12]  border-amber-500/25  ring-amber-500/20" },
  { value: "good",     emoji: "😊", label: "Gut",       color: "text-emerald-300 bg-emerald-500/[0.12] border-emerald-500/25 ring-emerald-500/20" },
  { value: "neutral",  emoji: "😐", label: "Okay",      color: "text-sky-300    bg-sky-500/[0.12]    border-sky-500/25    ring-sky-500/20" },
  { value: "bad",      emoji: "😞", label: "Schlecht",  color: "text-violet-300 bg-violet-500/[0.12] border-violet-500/25 ring-violet-500/20" },
  { value: "terrible", emoji: "😢", label: "Mies",      color: "text-rose-300   bg-rose-500/[0.12]   border-rose-500/25   ring-rose-500/20" },
];

const MOOD_DOT: Record<string, string> = {
  great: "bg-amber-400",
  good: "bg-emerald-400",
  neutral: "bg-sky-400",
  bad: "bg-violet-400",
  terrible: "bg-rose-400",
};

function getToday(): string {
  const now = new Date();
  const y = now.getFullYear();
  const m = String(now.getMonth() + 1).padStart(2, "0");
  const d = String(now.getDate()).padStart(2, "0");
  return `${y}-${m}-${d}`;
}

function formatDateLong(date: string): string {
  const d = new Date(date + "T00:00:00");
  return d.toLocaleDateString("de-DE", {
    weekday: "long", day: "numeric", month: "long", year: "numeric",
  });
}

function formatDateShort(date: string): string {
  const d = new Date(date + "T00:00:00");
  return d.toLocaleDateString("de-DE", { day: "numeric", month: "short" });
}

function formatWeekday(date: string): string {
  const d = new Date(date + "T00:00:00");
  return d.toLocaleDateString("de-DE", { weekday: "short" }).slice(0, 2);
}

function addDays(date: string, days: number): string {
  const d = new Date(date + "T00:00:00");
  d.setDate(d.getDate() + days);
  const year = d.getFullYear();
  const month = String(d.getMonth() + 1).padStart(2, "0");
  const day = String(d.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
}

function formatExportRangeLabel(range: string) {
  switch (range) {
    case "1": return "Heute";
    case "3": return "Letzte 3 Tage";
    case "7": return "Letzte 7 Tage";
    case "30": return "Letzte 30 Tage";
    case "90": return "Letzte 90 Tage";
    case "365": return "Letzte 365 Tage";
    default: return "Komplettes Journal";
  }
}

/** Last N dates ending at today */
function lastNDates(n: number): string[] {
  const today = getToday();
  return Array.from({ length: n }, (_, i) => addDays(today, -(n - 1 - i)));
}

/** An entry only counts as "written" if it has actual text or a mood set. */
function hasContent(entry: JournalEntry): boolean {
  return !!entry.mood || extractText(entry.content).length > 0;
}

/** Compute current writing streak — only days explicitly marked "Fertig" count. */
function computeStreak(entries: JournalEntry[]): number {
  const dateSet = new Set(entries.filter((e) => e.completed).map((e) => e.entry_date));
  let streak = 0;
  let cursor = getToday();
  while (dateSet.has(cursor)) {
    streak++;
    cursor = addDays(cursor, -1);
  }
  return streak;
}

export default function JournalPage() {
  const {
    entries,
    template,
    loading,
    saveTemplate,
    getOrCreateEntry,
    saveEntry,
  } = useJournal();

  const [selectedDate, setSelectedDate] = useState(getToday);
  const [currentEntry, setCurrentEntry] = useState<JournalEntry | null>(null);
  const [showTemplate, setShowTemplate] = useState(false);
  const [showArchive, setShowArchive] = useState(false);
  const [showExport, setShowExport] = useState(false);
  const [coverName, setCoverName] = useState("");
  const [exportRange, setExportRange] = useState<string>("all");
  const [loadingEntry, setLoadingEntry] = useState(false);
  const coverBg = useBackgroundPicker("mb-dark");
  const debounceTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
  const coverPreviewRef = useRef<HTMLDivElement>(null);
  const loadingRef = useRef(false);
  const isJournalVideoExport = coverBg.isCustomVideo && !!coverBg.customVideo;

  const isToday = selectedDate === getToday();
  const isFuture = selectedDate > getToday();
  const streak = computeStreak(entries);
  const week = lastNDates(7);
  const writtenEntries = entries.filter(hasContent);
  // Only entries explicitly marked "Fertig für heute" light up the heatmap.
  const completedEntries = entries.filter((e) => e.completed);
  const entryByDate = Object.fromEntries(completedEntries.map((e) => [e.entry_date, e]));

  // Load or create entry for selected date
  useEffect(() => {
    if (loading || isFuture) {
      if (isFuture) setCurrentEntry(null);
      return;
    }
    let cancelled = false;
    async function load() {
      if (loadingRef.current) return;
      loadingRef.current = true;
      setLoadingEntry(true);
      try {
        const entry = await getOrCreateEntry(selectedDate);
        if (!cancelled) setCurrentEntry(entry);
      } catch {
        if (!cancelled) setCurrentEntry(null);
      }
      if (!cancelled) setLoadingEntry(false);
      loadingRef.current = false;
    }
    load();
    return () => { cancelled = true; };
  }, [selectedDate, loading, isFuture, getOrCreateEntry]);

  // Entry editor
  const entryEditor = useEditor({
    immediatelyRender: false,
    extensions: [
      StarterKit.configure({ heading: { levels: [1, 2, 3] } }),
      Placeholder.configure({ placeholder: "Was bewegt dich heute? Schreib einfach drauf los…" }),
    ],
    content: currentEntry?.content || {},
    editable: !currentEntry?.completed,
    onUpdate: ({ editor }) => {
      if (!currentEntry) return;
      if (debounceTimer.current) clearTimeout(debounceTimer.current);
      const content = editor.getJSON();
      // Don't create a row for a day that was typed into and then cleared
      // again — only persist if there's real text, or the entry already
      // exists (a genuine edit to something saved before).
      if (!currentEntry.id && !extractText(content)) return;
      debounceTimer.current = setTimeout(() => {
        void saveEntry(currentEntry, { content }).then((saved) => {
          setCurrentEntry((prev) =>
            prev && prev.entry_date === saved.entry_date ? saved : prev
          );
        });
      }, 500);
    },
  });

  // Template editor
  const templateEditor = useEditor({
    immediatelyRender: false,
    extensions: [
      StarterKit.configure({ heading: { levels: [1, 2, 3] } }),
      Placeholder.configure({
        placeholder: "Vorlage für jeden neuen Eintrag – oder leer lassen.",
      }),
    ],
    content: template?.content || {},
  });

  // Sync entry editor when entry changes
  useEffect(() => {
    if (entryEditor && currentEntry) {
      const currentJSON = JSON.stringify(entryEditor.getJSON());
      const newJSON = JSON.stringify(currentEntry.content);
      if (currentJSON !== newJSON) {
        // emitUpdate defaults to true in Tiptap v3 — without this, loading
        // a different day's content fires onUpdate as if the user had
        // typed it, saving untouched (e.g. template-prefilled) days.
        entryEditor.commands.setContent(currentEntry.content, { emitUpdate: false });
      }
    }
  }, [currentEntry, entryEditor]);

  // Lock the editor once a day is marked "Fertig für heute"
  useEffect(() => {
    entryEditor?.setEditable(!currentEntry?.completed);
  }, [entryEditor, currentEntry?.completed]);

  // Sync template editor
  useEffect(() => {
    if (templateEditor && template) {
      templateEditor.commands.setContent(template.content);
    }
  }, [template, templateEditor]);

  function handleSaveTemplate() {
    if (templateEditor) {
      saveTemplate(templateEditor.getJSON());
      setShowTemplate(false);
    }
  }

  async function handleMood(mood: Mood) {
    if (!currentEntry) return;
    const newMood = currentEntry.mood === mood ? null : mood;
    const latestContent = entryEditor ? entryEditor.getJSON() : currentEntry.content;
    setCurrentEntry({ ...currentEntry, content: latestContent, mood: newMood });
    const saved = await saveEntry(currentEntry, { content: latestContent, mood: newMood });
    setCurrentEntry((prev) =>
      prev && prev.entry_date === saved.entry_date ? saved : prev
    );
  }

  async function handleComplete() {
    if (!currentEntry) return;
    if (debounceTimer.current) clearTimeout(debounceTimer.current);
    const latestContent = entryEditor ? entryEditor.getJSON() : currentEntry.content;
    const saved = await saveEntry(currentEntry, { content: latestContent, completed: true });
    setCurrentEntry((prev) =>
      prev && prev.entry_date === saved.entry_date ? saved : prev
    );
  }

  async function handleReopen() {
    if (!currentEntry?.id) return;
    const saved = await saveEntry(currentEntry, { completed: false });
    setCurrentEntry((prev) =>
      prev && prev.entry_date === saved.entry_date ? saved : prev
    );
  }

  function goBack() { setSelectedDate((prev) => addDays(prev, -1)); }
  function goForward() {
    setSelectedDate((prev) => {
      const next = addDays(prev, 1);
      return next > getToday() ? prev : next;
    });
  }

  async function buildJournalPdfFile() {
    const { default: jsPDF } = await import("jspdf");
    const doc = new jsPDF({ unit: "mm", format: "a4" });
    const pageW = doc.internal.pageSize.getWidth();
    const pageH = doc.internal.pageSize.getHeight();
    const margin = 20;
    const contentW = pageW - margin * 2;

    const today = new Date();
    const cutoffDate = new Date(today);
    switch (exportRange) {
      case "1": cutoffDate.setDate(today.getDate()); break;
      case "3": cutoffDate.setDate(today.getDate() - 2); break;
      case "7": cutoffDate.setDate(today.getDate() - 6); break;
      case "30": cutoffDate.setDate(today.getDate() - 29); break;
      case "90": cutoffDate.setDate(today.getDate() - 89); break;
      case "365": cutoffDate.setDate(today.getDate() - 364); break;
      default: cutoffDate.setFullYear(2000); break;
    }
    const cutoff = `${cutoffDate.getFullYear()}-${String(cutoffDate.getMonth() + 1).padStart(2, "0")}-${String(cutoffDate.getDate()).padStart(2, "0")}`;
    const sorted = writtenEntries.filter((e) => e.entry_date >= cutoff).sort((a, b) => a.entry_date.localeCompare(b.entry_date));

    const coverPreset = BG_PRESETS.find((p) => p.id === coverBg.presetId);
    const isDarkCover = coverPreset ? coverPreset.textColor === "#ffffff" : true;
    function hexToRgb(hex: string) {
      const h = hex.replace("#", "");
      return { r: parseInt(h.substring(0, 2), 16), g: parseInt(h.substring(2, 4), 16), b: parseInt(h.substring(4, 6), 16) };
    }
    const accent = hexToRgb(coverPreset?.accentColor || "#6366f1");

    if (coverBg.isCustom && coverBg.customImage) {
      try {
        doc.addImage(coverBg.customImage, "JPEG", 0, 0, pageW, pageH);
        doc.setFillColor(0, 0, 0);
        doc.setGState(new (doc as unknown as { GState: new (opts: { opacity: number }) => unknown }).GState({ opacity: 0.45 }));
        doc.rect(0, 0, pageW, pageH, "F");
        doc.setGState(new (doc as unknown as { GState: new (opts: { opacity: number }) => unknown }).GState({ opacity: 1 }));
      } catch {
        doc.setFillColor(5, 5, 8);
        doc.rect(0, 0, pageW, pageH, "F");
      }
    } else if (isDarkCover) {
      doc.setFillColor(5, 5, 8);
      doc.rect(0, 0, pageW, pageH, "F");
    } else {
      doc.setFillColor(250, 250, 252);
      doc.rect(0, 0, pageW, pageH, "F");
    }

    for (let i = 0; i < 40; i++) {
      const alpha = 1 - i / 40;
      doc.setDrawColor(Math.round(accent.r * alpha), Math.round(accent.g * alpha), Math.round(accent.b * alpha));
      doc.setLineWidth(0.6);
      doc.circle(pageW + 20, -20, 60 + i * 2.5, "S");
    }
    for (let i = 0; i < 30; i++) {
      const alpha = 1 - i / 30;
      doc.setDrawColor(Math.round(accent.r * 0.7 * alpha + 100 * alpha), Math.round(accent.g * 0.5 * alpha), Math.round(accent.b * alpha));
      doc.setLineWidth(0.4);
      doc.circle(-30, pageH + 30, 80 + i * 2, "S");
    }

    doc.setDrawColor(accent.r, accent.g, accent.b);
    doc.setLineWidth(0.8);
    doc.line(margin, pageH * 0.38, margin + 40, pageH * 0.38);
    doc.setTextColor(isDarkCover ? 80 : 160, isDarkCover ? 80 : 160, isDarkCover ? 90 : 170);
    doc.setFontSize(9);
    doc.text("MINDBOARD", margin, margin + 8);
    doc.setFillColor(isDarkCover ? 30 : 210, isDarkCover ? 30 : 210, isDarkCover ? 40 : 220);
    for (let row = 0; row < 5; row++) for (let col = 0; col < 5; col++) doc.circle(pageW - margin - col * 5, margin + 5 + row * 5, 0.6, "F");

    const displayName = coverName || "Mein";
    doc.setTextColor(isDarkCover ? 250 : 20, isDarkCover ? 250 : 20, isDarkCover ? 250 : 30);
    doc.setFontSize(52);
    doc.text(displayName, margin, pageH * 0.44);
    doc.setFontSize(52);
    doc.setTextColor(isDarkCover ? 200 : 80, isDarkCover ? 200 : 80, isDarkCover ? 210 : 100);
    doc.text("Journal", margin, pageH * 0.44 + 18);
    doc.setFontSize(13);
    doc.setTextColor(isDarkCover ? 120 : 140, isDarkCover ? 120 : 140, isDarkCover ? 140 : 160);
    if (sorted.length > 0) {
      const first = sorted[0].entry_date;
      const last = sorted[sorted.length - 1].entry_date;
      doc.text(first === last ? formatDateLong(first) : `${formatDateLong(first)} — ${formatDateLong(last)}`, margin, pageH * 0.44 + 32);
    } else {
      doc.text(new Date().getFullYear().toString(), margin, pageH * 0.44 + 32);
    }

    const bottomY = pageH - margin - 8;
    doc.setFontSize(28);
    doc.setTextColor(isDarkCover ? 250 : 20, isDarkCover ? 250 : 20, isDarkCover ? 250 : 30);
    doc.text(sorted.length.toString(), margin, bottomY);
    doc.setFontSize(8);
    doc.setTextColor(isDarkCover ? 100 : 140, isDarkCover ? 100 : 140, isDarkCover ? 120 : 160);
    doc.text("EINTRÄGE", margin, bottomY + 5);

    const moodCounts = { great: 0, good: 0, neutral: 0, bad: 0, terrible: 0 };
    for (const entry of sorted) if (entry.mood && entry.mood in moodCounts) moodCounts[entry.mood as keyof typeof moodCounts]++;
    const topMood = Object.entries(moodCounts).sort((a, b) => b[1] - a[1])[0];
    const moodNames: Record<string, string> = { great: "Super", good: "Gut", neutral: "Okay", bad: "Schlecht", terrible: "Mies" };
    if (topMood && topMood[1] > 0) {
      doc.setFontSize(28);
      doc.setTextColor(isDarkCover ? 250 : 20, isDarkCover ? 250 : 20, isDarkCover ? 250 : 30);
      doc.text(moodNames[topMood[0]] || "—", margin + 50, bottomY);
      doc.setFontSize(8);
      doc.setTextColor(isDarkCover ? 100 : 140, isDarkCover ? 100 : 140, isDarkCover ? 120 : 160);
      doc.text("TOP STIMMUNG", margin + 50, bottomY + 5);
    }

    const rangeDays = exportRange === "all" ? sorted.length : parseInt(exportRange) || sorted.length;
    doc.setFontSize(28);
    doc.setTextColor(isDarkCover ? 250 : 20, isDarkCover ? 250 : 20, isDarkCover ? 250 : 30);
    doc.text(rangeDays.toString(), pageW - margin - 30, bottomY);
    doc.setFontSize(8);
    doc.setTextColor(isDarkCover ? 100 : 140, isDarkCover ? 100 : 140, isDarkCover ? 120 : 160);
    doc.text("TAGE", pageW - margin - 30, bottomY + 5);
    doc.setDrawColor(accent.r, accent.g, accent.b);
    doc.setLineWidth(2);
    doc.line(0, pageH - 3, pageW, pageH - 3);

    for (let ei = 0; ei < sorted.length; ei++) {
      const entry = sorted[ei];
      doc.addPage();
      doc.setFillColor(255, 255, 255);
      doc.rect(0, 0, pageW, pageH, "F");
      doc.setFillColor(99, 102, 241);
      doc.rect(0, 0, pageW, 1.5, "F");
      let y = margin + 5;
      doc.setTextColor(17, 17, 17);
      doc.setFontSize(20);
      doc.text(formatDateLong(entry.entry_date), margin, y);
      y += 10;
      if (entry.mood) {
        const moodLabel = MOODS.find((m) => m.value === entry.mood)?.label || entry.mood;
        doc.setFillColor(245, 245, 250);
        doc.roundedRect(margin, y - 3, 35, 7, 2, 2, "F");
        doc.setFontSize(9);
        doc.setTextColor(99, 102, 241);
        doc.text(`Stimmung: ${moodLabel}`, margin + 2, y + 2);
        y += 12;
      } else { y += 4; }
      doc.setDrawColor(240, 240, 245);
      doc.setLineWidth(0.3);
      doc.line(margin, y, pageW - margin, y);
      y += 10;
      const text = extractText(entry.content);
      if (text) {
        doc.setFontSize(10.5);
        doc.setTextColor(50, 50, 55);
        const lines = doc.splitTextToSize(text, contentW);
        for (const line of lines) {
          if (y > pageH - 20) {
            doc.addPage();
            doc.setFillColor(255, 255, 255);
            doc.rect(0, 0, pageW, pageH, "F");
            y = margin;
          }
          doc.text(line, margin, y);
          y += 5.5;
        }
      }
      doc.setFontSize(7);
      doc.setTextColor(180, 180, 195);
      doc.text("Mindavio Journal", margin, pageH - 10);
      doc.text(`${ei + 1} / ${sorted.length}`, pageW - margin, pageH - 10, { align: "right" });
    }

    const fileName = `journal-${getToday()}-${exportRange === "all" ? "all" : `${exportRange}d`}.pdf`;
    return new File([doc.output("blob")], fileName, { type: "application/pdf" });
  }

  async function buildJournalReviewPayload() {
    if (isJournalVideoExport && coverBg.customVideo && coverPreviewRef.current) {
      const dateKey = getToday();
      return {
        files: [
          await captureElementWithVideoBackground(coverPreviewRef.current, {
            durationMs: 6500,
            fileName: `Mindavio-Journal-Review-${dateKey}.webm`,
            fps: 30,
            pixelRatio: 2,
            videoSrc: coverBg.customVideo,
          }),
        ],
        message: `Hier ist mein Journal Review als Video (${formatExportRangeLabel(exportRange)}).`,
      };
    }
    return {
      files: [await buildJournalPdfFile()],
      message: `Hier ist mein Journal als PDF (${formatExportRangeLabel(exportRange)}).`,
    };
  }

  if (loading) {
    return (
      <div className="flex h-[calc(100dvh-72px)] gap-5 p-1">
        <Skeleton className="w-60 h-full rounded-2xl" />
        <Skeleton className="flex-1 h-full rounded-2xl" />
      </div>
    );
  }

  const activeMoodMeta = currentEntry?.mood ? MOODS.find((m) => m.value === currentEntry.mood) : null;

  return (
    <div className="flex gap-5 h-[calc(100dvh-72px)] overflow-hidden -mt-1">

      {/* ── Left Sidebar ─────────────────────────────────────────── */}
      <aside className="hidden lg:flex w-56 shrink-0 flex-col gap-3">

        {/* Streak card */}
        <div className="rounded-2xl border border-white/[0.06] bg-white/[0.02] p-4">
          <div className="flex items-center gap-2 mb-3">
            <Flame className="h-4 w-4 text-orange-400" />
            <span className="text-[11px] font-semibold uppercase tracking-widest text-white/30">Streak</span>
          </div>
          <div className="flex items-end gap-1.5">
            <span className="text-4xl font-bold text-white leading-none">{streak}</span>
            <span className="text-xs text-white/30 mb-1">Tage</span>
          </div>
          <p className="text-[11px] text-white/20 mt-1.5">
            {streak === 0 ? "Fang heute an!" : streak === 1 ? "Guter Start!" : "Weiter so 🔥"}
          </p>
        </div>

        {/* Week heatmap */}
        <div className="rounded-2xl border border-white/[0.06] bg-white/[0.02] p-4">
          <p className="text-[11px] font-semibold uppercase tracking-widest text-white/30 mb-3">Diese Woche</p>
          <div className="flex items-center justify-between">
            {week.map((date) => {
              const e = entryByDate[date];
              const isSelected = date === selectedDate;
              return (
                <button
                  key={date}
                  onClick={() => { if (date <= getToday()) setSelectedDate(date); }}
                  disabled={date > getToday()}
                  className={cn(
                    "flex flex-col items-center gap-1.5 cursor-pointer transition-opacity",
                    date > getToday() ? "opacity-20 cursor-default" : "hover:opacity-80"
                  )}
                >
                  <span className="text-[9px] text-white/25 font-medium uppercase">{formatWeekday(date)}</span>
                  <div className={cn(
                    "w-6 h-6 rounded-full flex items-center justify-center transition-all",
                    isSelected
                      ? "ring-2 ring-emerald-400/60 ring-offset-1 ring-offset-[#0a0a0d]"
                      : ""
                  )}>
                    {e?.mood ? (
                      <div className={cn("w-4 h-4 rounded-full", MOOD_DOT[e.mood])} />
                    ) : e ? (
                      <div className="w-4 h-4 rounded-full bg-emerald-400/70" />
                    ) : (
                      <div className="w-4 h-4 rounded-full border border-white/[0.06]" />
                    )}
                  </div>
                </button>
              );
            })}
          </div>
        </div>

        {/* Recent entries */}
        <div className="rounded-2xl border border-white/[0.06] bg-white/[0.02] p-4 flex-1 flex flex-col min-h-0">
          <p className="text-[11px] font-semibold uppercase tracking-widest text-white/30 mb-3 shrink-0">Einträge</p>
          <div className="flex-1 overflow-y-auto space-y-0.5 scrollbar-none">
            {writtenEntries.length === 0 ? (
              <p className="text-[11px] text-white/20 py-4 text-center">Noch nichts.</p>
            ) : (
              [...writtenEntries]
                .sort((a, b) => b.entry_date.localeCompare(a.entry_date))
                .slice(0, 30)
                .map((e) => (
                  <button
                    key={e.id}
                    onClick={() => setSelectedDate(e.entry_date)}
                    className={cn(
                      "w-full text-left rounded-lg px-2.5 py-2 transition-colors cursor-pointer",
                      e.entry_date === selectedDate
                        ? "bg-emerald-500/[0.10] text-emerald-100"
                        : "text-white/35 hover:bg-white/[0.04] hover:text-white/60"
                    )}
                  >
                    <div className="flex items-center gap-2">
                      {e.mood ? (
                        <span className="text-[13px] leading-none">{MOODS.find((m) => m.value === e.mood)?.emoji}</span>
                      ) : (
                        <PenLine className="h-3 w-3 shrink-0 opacity-40" />
                      )}
                      <div className="min-w-0">
                        <div className="text-[11px] font-medium truncate">
                          {e.entry_date === getToday() ? "Heute" : formatDateShort(e.entry_date)}
                        </div>
                        <div className="text-[10px] opacity-50 truncate">
                          {extractText(e.content).slice(0, 28) || "Leerer Eintrag"}
                        </div>
                      </div>
                    </div>
                  </button>
                ))
            )}
          </div>
        </div>

        {/* Sidebar actions */}
        <div className="space-y-1 shrink-0">
          {[
            { icon: Settings2, label: "Vorlage", action: () => setShowTemplate(true) },
            { icon: FileText, label: "Archiv", action: () => setShowArchive(true) },
            { icon: Download, label: "Export", action: () => setShowExport(true) },
          ].map(({ icon: Icon, label, action }) => (
            <button
              key={label}
              onClick={action}
              className="w-full flex items-center gap-2.5 rounded-xl px-3 py-2 text-[12px] font-medium text-white/30 hover:bg-white/[0.04] hover:text-white/60 transition-colors cursor-pointer"
            >
              <Icon className="h-3.5 w-3.5" />
              {label}
            </button>
          ))}
        </div>
      </aside>

      {/* ── Main Writing Area ─────────────────────────────────────── */}
      <main className="flex-1 flex flex-col min-w-0 rounded-2xl border border-white/[0.06] bg-white/[0.02] overflow-hidden">

        {/* Top bar */}
        <div className="flex items-center justify-between px-6 py-4 border-b border-white/[0.05] shrink-0">
          {/* Date navigation */}
          <div className="flex items-center gap-2">
            <button
              onClick={goBack}
              className="h-7 w-7 rounded-lg flex items-center justify-center text-white/25 hover:bg-white/[0.06] hover:text-white/60 transition-colors cursor-pointer"
            >
              <ChevronLeft className="h-4 w-4" />
            </button>
            <button
              onClick={() => setSelectedDate(getToday())}
              className={cn(
                "px-3 py-1 rounded-lg text-sm font-semibold transition-colors cursor-pointer",
                isToday
                  ? "bg-emerald-500/[0.12] text-emerald-300 border border-emerald-500/25"
                  : "text-white/50 hover:bg-white/[0.04] hover:text-white/80"
              )}
            >
              {isToday ? "Heute" : formatDateShort(selectedDate)}
              {!isToday && (
                <span className="ml-2 text-xs text-white/25">
                  {new Date(selectedDate + "T00:00:00").toLocaleDateString("de-DE", { weekday: "short" })}
                </span>
              )}
            </button>
            <button
              onClick={goForward}
              disabled={isToday || isFuture}
              className="h-7 w-7 rounded-lg flex items-center justify-center text-white/25 hover:bg-white/[0.06] hover:text-white/60 transition-colors cursor-pointer disabled:opacity-20 disabled:cursor-default"
            >
              <ChevronRight className="h-4 w-4" />
            </button>
          </div>

          {/* Right actions */}
          <div className="flex items-center gap-1">
            {/* Mobile-only actions */}
            <button onClick={() => setShowTemplate(true)} className="lg:hidden h-8 w-8 flex items-center justify-center rounded-lg text-white/25 hover:bg-white/[0.06] hover:text-white/50 transition-colors cursor-pointer">
              <Settings2 className="h-3.5 w-3.5" />
            </button>
            <button onClick={() => setShowArchive(true)} className="lg:hidden h-8 w-8 flex items-center justify-center rounded-lg text-white/25 hover:bg-white/[0.06] hover:text-white/50 transition-colors cursor-pointer">
              <FileText className="h-3.5 w-3.5" />
            </button>
            <button onClick={() => setShowExport(true)} className="lg:hidden h-8 w-8 flex items-center justify-center rounded-lg text-white/25 hover:bg-white/[0.06] hover:text-white/50 transition-colors cursor-pointer">
              <Download className="h-3.5 w-3.5" />
            </button>
            {currentEntry && !isFuture && (
              currentEntry.completed ? (
                <div className="flex items-center gap-1.5">
                  <span className="flex items-center gap-1.5 rounded-lg border border-emerald-500/25 bg-emerald-500/[0.12] px-3 py-1.5 text-xs font-semibold text-emerald-300">
                    <Lock className="h-3 w-3" />
                    Fertig
                  </span>
                  <button
                    onClick={handleReopen}
                    title="Eintrag wieder bearbeiten"
                    className="flex items-center gap-1.5 rounded-lg border border-white/[0.08] bg-transparent px-2.5 py-1.5 text-xs font-medium text-white/40 hover:bg-white/[0.06] hover:text-white/70 transition-colors cursor-pointer"
                  >
                    <Pencil className="h-3 w-3" />
                    Bearbeiten
                  </button>
                </div>
              ) : (
                <button
                  onClick={handleComplete}
                  className="flex items-center gap-1.5 rounded-lg border border-white/[0.08] bg-white/[0.03] px-3 py-1.5 text-xs font-semibold text-white/50 hover:border-emerald-500/25 hover:bg-emerald-500/[0.10] hover:text-emerald-300 transition-colors cursor-pointer"
                >
                  <Check className="h-3.5 w-3.5" />
                  Fertig für heute
                </button>
              )
            )}
            {currentEntry?.id && (
              <>
                <RemindMeButton
                  body={extractText(currentEntry.content).slice(0, 240)}
                  className="border-white/[0.06] bg-transparent text-white/30 hover:bg-white/[0.05] hover:text-white/60 h-8 text-xs"
                  label="Erinnern"
                  sourceId={currentEntry.id}
                  sourceTitle={`Journal ${formatDateLong(currentEntry.entry_date)}`}
                  sourceType="journal"
                  sourceUrl="/journal"
                  title={`Journal erinnern: ${formatDateLong(currentEntry.entry_date)}`}
                />
                <ShareCardButton
                  variant="pill"
                  data={{
                    type: "journal",
                    date: selectedDate,
                    mood: currentEntry.mood ?? undefined,
                    excerpt: currentEntry.content
                      ? (() => {
                          const chunks: string[] = [];
                          function walk(v: unknown): void {
                            if (!v || typeof v !== "object") return;
                            if ("text" in v && typeof (v as Record<string, unknown>).text === "string") chunks.push((v as Record<string, string>).text);
                            if ("content" in v && Array.isArray((v as Record<string, unknown>).content)) ((v as Record<string, unknown[]>).content as unknown[]).forEach(walk);
                          }
                          walk(currentEntry.content);
                          return chunks.join(" ").trim().slice(0, 120) || null;
                        })()
                      : null,
                    tags: [],
                  }}
                />
              </>
            )}
          </div>
        </div>

        {/* Date heading */}
        {!isFuture && (
          <div className="px-8 pt-6 pb-4 shrink-0">
            <h2 className="text-2xl font-bold text-white/80 tracking-tight">
              {isToday ? "Heute" : formatDateLong(selectedDate)}
            </h2>
            {isToday && (
              <p className="text-sm text-white/25 mt-0.5">{formatDateLong(selectedDate)}</p>
            )}
          </div>
        )}

        {/* Mood row */}
        {currentEntry && !isFuture && (
          <div className="px-8 pb-5 shrink-0">
            <div className="flex items-center gap-2">
              <span className="text-[11px] text-white/20 mr-1 font-medium uppercase tracking-widest">Stimmung</span>
              {MOODS.map((m) => {
                const isActive = currentEntry.mood === m.value;
                return (
                  <button
                    key={m.value}
                    onClick={() => handleMood(m.value)}
                    disabled={currentEntry.completed}
                    title={m.label}
                    className={cn(
                      "flex items-center gap-1.5 rounded-full px-2.5 py-1 text-[12px] font-medium border transition-all cursor-pointer disabled:cursor-default disabled:opacity-50",
                      isActive
                        ? m.color + " ring-2 scale-105"
                        : "text-white/25 border-white/[0.06] bg-transparent hover:bg-white/[0.05] hover:text-white/50"
                    )}
                  >
                    <span className="text-[14px] leading-none">{m.emoji}</span>
                    {isActive && <span className="text-[11px]">{m.label}</span>}
                  </button>
                );
              })}
              {activeMoodMeta && !currentEntry.completed && (
                <button
                  onClick={() => handleMood(activeMoodMeta.value)}
                  className="ml-1 text-[10px] text-white/15 hover:text-white/30 transition-colors cursor-pointer"
                >
                  ✕
                </button>
              )}
            </div>
          </div>
        )}

        {/* Divider */}
        {!isFuture && <div className="mx-8 border-t border-white/[0.05] shrink-0" />}

        {/* Editor area */}
        <div className="flex-1 overflow-y-auto px-8 py-6 min-h-0">
          {isFuture ? (
            <div className="flex flex-col items-center justify-center h-full gap-3 text-center">
              <Calendar className="h-10 w-10 text-white/10" />
              <p className="text-sm text-white/20">Keine Einträge für die Zukunft.</p>
            </div>
          ) : loadingEntry ? (
            <div className="space-y-3">
              <Skeleton className="h-4 w-3/4 rounded-lg" />
              <Skeleton className="h-4 w-full rounded-lg" />
              <Skeleton className="h-4 w-5/6 rounded-lg" />
              <Skeleton className="h-4 w-2/3 rounded-lg" />
            </div>
          ) : (
            <EditorContent
              editor={entryEditor}
              className={cn(
                "tiptap journal-editor prose prose-invert prose-sm max-w-none focus:outline-none",
                currentEntry?.completed && "opacity-60 cursor-default"
              )}
            />
          )}
        </div>
      </main>

      {/* ── Template Modal ──────────────────────────────────────────── */}
      {showTemplate && (
        <Modal open={showTemplate} onClose={() => setShowTemplate(false)} title="Journal-Vorlage" className="max-w-2xl">
          <p className="text-sm text-white/40 mb-4">
            Diese Vorlage wird automatisch für neue Einträge verwendet. Lass sie leer, wenn du keine möchtest.
          </p>
          <div className="rounded-xl border border-white/[0.06] bg-white/[0.02] p-4 min-h-[250px] mb-4">
            <EditorContent editor={templateEditor} className="tiptap prose prose-invert prose-sm max-w-none min-h-[200px] focus:outline-none" />
          </div>
          <div className="flex justify-end gap-2">
            <Button variant="ghost" onClick={() => setShowTemplate(false)}>Abbrechen</Button>
            <Button onClick={handleSaveTemplate}>Speichern</Button>
          </div>
        </Modal>
      )}

      {/* ── Archive Modal ───────────────────────────────────────────── */}
      {showArchive && (
        <Modal open={showArchive} onClose={() => setShowArchive(false)} title="Journal-Archiv" className="max-w-lg">
          <div className="space-y-0.5 max-h-[420px] overflow-y-auto">
            {writtenEntries.length === 0 ? (
              <p className="text-sm text-white/30 py-8 text-center">Noch keine Einträge.</p>
            ) : (
              [...writtenEntries]
                .sort((a, b) => b.entry_date.localeCompare(a.entry_date))
                .map((entry) => (
                  <button
                    key={entry.id}
                    onClick={() => { setSelectedDate(entry.entry_date); setShowArchive(false); }}
                    className={cn(
                      "w-full text-left px-3 py-2.5 rounded-lg flex items-center gap-3 transition-colors cursor-pointer",
                      entry.entry_date === selectedDate
                        ? "bg-emerald-500/[0.10] text-emerald-100"
                        : "text-white/40 hover:bg-white/[0.04] hover:text-white/70"
                    )}
                  >
                    <div className="text-[16px] leading-none w-5 text-center">
                      {entry.mood ? MOODS.find((m) => m.value === entry.mood)?.emoji : <BookOpen className="h-3.5 w-3.5 text-white/20" />}
                    </div>
                    <div className="flex-1 min-w-0">
                      <div className="text-[13px] font-medium">{formatDateLong(entry.entry_date)}</div>
                      <div className="text-[11px] opacity-40 truncate">{extractText(entry.content).slice(0, 80) || "Leerer Eintrag"}</div>
                    </div>
                  </button>
                ))
            )}
          </div>
        </Modal>
      )}

      {/* ── Export Modal ────────────────────────────────────────────── */}
      {showExport && (
        <Modal open={showExport} onClose={() => setShowExport(false)} title="Journal exportieren" className="max-w-3xl">
          <ReviewShareStudio
            artifactType="journal"
            buildPayload={buildJournalReviewPayload}
            controls={
              <div className="grid gap-4 lg:grid-cols-[1fr_0.9fr]">
                <div className="space-y-4">
                  <div className="space-y-2">
                    <label className="text-sm font-medium text-white/75">Zeitraum</label>
                    <div className="grid grid-cols-2 gap-1.5 sm:grid-cols-4">
                      {[
                        { value: "1", label: "Heute" },
                        { value: "3", label: "3 Tage" },
                        { value: "7", label: "7 Tage" },
                        { value: "30", label: "30 Tage" },
                        { value: "90", label: "90 Tage" },
                        { value: "365", label: "365 Tage" },
                        { value: "all", label: "Alles" },
                      ].map((opt) => (
                        <button
                          key={opt.value}
                          onClick={() => setExportRange(opt.value)}
                          className={cn(
                            "cursor-pointer rounded-lg border px-2 py-1.5 text-xs font-medium transition-colors",
                            exportRange === opt.value
                              ? "border-amber-400/35 bg-amber-500/[0.14] text-amber-100"
                              : "border-white/[0.08] bg-white/[0.03] text-white/45 hover:bg-white/[0.06] hover:text-white/70",
                          )}
                        >
                          {opt.label}
                        </button>
                      ))}
                    </div>
                  </div>
                  <div className="space-y-2">
                    <label className="text-sm font-medium text-white/75">Name auf dem Cover</label>
                    <input
                      type="text"
                      value={coverName}
                      onChange={(e) => setCoverName(e.target.value)}
                      placeholder="Dein Name"
                      className="w-full rounded-lg border border-white/[0.08] bg-white/[0.03] px-3 py-2 text-sm text-white outline-none placeholder:text-white/25 focus:ring-2 focus:ring-amber-500/25"
                    />
                  </div>
                </div>
                <BackgroundPicker
                  presetId={coverBg.presetId}
                  customImage={coverBg.customImage}
                  customVideo={coverBg.customVideo}
                  onSelectPreset={coverBg.setPresetId}
                  onUpload={coverBg.handleUpload}
                  onRemoveCustom={coverBg.removeCustom}
                />
              </div>
            }
            defaultMessage={
              isJournalVideoExport
                ? `Hier ist mein Journal Review als Video (${formatExportRangeLabel(exportRange)}).`
                : `Hier ist mein Journal als PDF (${formatExportRangeLabel(exportRange)}).`
            }
            description={
              isJournalVideoExport
                ? "Exportiere dein Journal Cover als Video Review"
                : "Exportiere dein Journal als PDF mit Cover-Seite"
            }
            downloadLabel={isJournalVideoExport ? "Als Video speichern" : "PDF herunterladen"}
            icon={isJournalVideoExport ? <Video className="h-4 w-4 text-white" /> : <FileText className="h-4 w-4 text-white" />}
            onDownloaded={() => setShowExport(false)}
            shareDescription={
              isJournalVideoExport
                ? "Erstellt dein Journal-Video und sendet es direkt als Chat-Anhang."
                : "Erstellt dein Journal-PDF und sendet es direkt als Chat-Anhang."
            }
            shareTitle="Journal an Chat senden"
            title="Journal Review Export"
            tone="amber"
          >
            <div className="rounded-xl overflow-hidden border border-white/[0.08]">
              <div
                ref={coverPreviewRef}
                className="aspect-[3/4] max-h-[420px] relative overflow-hidden"
                style={coverBg.bgStyle}
              >
                <BackgroundVideoLayer src={coverBg.isCustomVideo ? coverBg.customVideo : null} />
                {coverBg.isCustom && <div className="absolute inset-0 bg-black/40" />}
                <div className="absolute -top-8 -right-8 w-40 h-40 rounded-full opacity-20" style={{ background: `radial-gradient(circle, ${coverBg.theme.accentColor} 0%, transparent 70%)` }} />
                <div className="absolute -bottom-10 -left-10 w-44 h-44 rounded-full opacity-15" style={{ background: `radial-gradient(circle, ${coverBg.theme.accentColor} 0%, transparent 70%)` }} />
                <div className="absolute inset-0 flex items-center justify-center pointer-events-none" style={{ opacity: coverBg.theme.showLogo ? 0.55 : 0.35 }}>
                  {!isJournalVideoExport && <MindavioLogo color={coverBg.theme.accentColor} size={240} />}
                </div>
                <span className="absolute top-4 left-5 tracking-[0.15em] uppercase" style={{ color: coverBg.theme.subtleColor, fontSize: "8px" }}>Mindavio</span>
                <div className="absolute top-4 right-5 grid grid-cols-4 gap-[3px]">
                  {Array.from({ length: 16 }).map((_, i) => (
                    <div key={i} className="w-1 h-1 rounded-full" style={{ backgroundColor: `${coverBg.theme.accentColor}20` }} />
                  ))}
                </div>
                <div className="absolute left-5 w-8 h-[2px]" style={{ top: "42%", backgroundColor: coverBg.theme.accentColor }} />
                <div className="absolute left-5" style={{ top: "46%" }}>
                  <div className="font-bold leading-none" style={{ fontSize: "28px", color: coverBg.theme.textColor }}>{coverName || "Mein"}</div>
                  <div className="font-bold leading-none mt-1" style={{ fontSize: "28px", color: coverBg.theme.subtleColor }}>Journal</div>
                  <div className="mt-2" style={{ fontSize: "8px", color: coverBg.theme.subtleColor }}>
                    {exportRange === "all" ? `${writtenEntries.length} Einträge` : exportRange === "1" ? "Heute" : `Letzte ${exportRange} Tage`}
                  </div>
                </div>
                <div className="absolute bottom-4 left-5 right-5 flex items-end justify-between">
                  <div>
                    <div className="font-bold leading-none" style={{ fontSize: "18px", color: coverBg.theme.textColor }}>{writtenEntries.length}</div>
                    <div className="uppercase tracking-wider mt-0.5" style={{ fontSize: "6px", color: coverBg.theme.subtleColor }}>Einträge</div>
                  </div>
                  <div>
                    <div className="font-bold leading-none" style={{ fontSize: "18px", color: coverBg.theme.textColor }}>{exportRange === "all" ? writtenEntries.length : exportRange}</div>
                    <div className="uppercase tracking-wider mt-0.5" style={{ fontSize: "6px", color: coverBg.theme.subtleColor }}>Tage</div>
                  </div>
                </div>
                <div className="absolute bottom-0 left-0 right-0 h-[2px]" style={{ backgroundColor: coverBg.theme.accentColor }} />
              </div>
            </div>
          </ReviewShareStudio>
        </Modal>
      )}
    </div>
  );
}

function extractText(content: Record<string, unknown>): string {
  let text = "";
  try {
    const doc = content as { content?: Array<{ content?: Array<{ text?: string }> }> };
    if (doc?.content) {
      for (const block of doc.content) {
        if (block.content) {
          for (const inline of block.content) {
            if (inline.text) text += inline.text + " ";
          }
        }
      }
    }
  } catch {}
  return text.trim();
}
