"use client";

import { useState, useCallback, useEffect, useRef } from "react";
import type { ReactNode } from "react";
import { clientExecute } from "@/lib/api/client";
import { InlineLoader } from "@/components/Loading";

// Format an API timestamp (ISO/date) into a short display string.
function fmtTime(v: string | Date | undefined): string {
  if (!v) return "";
  const d = new Date(v);
  if (isNaN(d.getTime())) return String(v);
  const today = new Date();
  const sameDay = d.toDateString() === today.toDateString();
  return sameDay
    ? d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })
    : d.toLocaleDateString([], { month: "short", day: "numeric" });
}

// ─────────────────────────────────────────────────────────────────────────────
// Types
// ─────────────────────────────────────────────────────────────────────────────

export interface ChatMessage {
  id: string;
  sender: "me" | "them";
  text: string;
  time: string;
}

export interface Conversation {
  id: string;
  with: string;
  role: string;
  avatar: string;
  propertyTitle: string;
  propertyAddress: string;
  unread: boolean;
  lastMessage: string;
  lastTime: string;
  messages: ChatMessage[];
}

interface ConfirmOptions {
  title: string;
  body: string;
  confirmLabel?: string;
  danger?: boolean;
}

// ─────────────────────────────────────────────────────────────────────────────
// useToast
// ─────────────────────────────────────────────────────────────────────────────

type ToastVariant = "success" | "error" | "info";

interface ToastItem {
  id: number;
  message: string;
  variant: ToastVariant;
  title?: string;
}

const TOAST_DURATION = 4200;

const TOAST_META: Record<ToastVariant, { label: string; ring: string; bar: string; icon: ReactNode }> = {
  success: {
    label: "Success",
    ring: "bg-forest",
    bar: "bg-forest",
    icon: (
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" className="w-4 h-4">
        <path d="M5 13l4 4L19 7" strokeLinecap="round" strokeLinejoin="round" />
      </svg>
    ),
  },
  error: {
    label: "Something went wrong",
    ring: "bg-clay",
    bar: "bg-clay",
    icon: (
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" className="w-4 h-4">
        <path d="M6 6l12 12M18 6L6 18" strokeLinecap="round" strokeLinejoin="round" />
      </svg>
    ),
  },
  info: {
    label: "Heads up",
    ring: "bg-gold-deep",
    bar: "bg-gold-deep",
    icon: (
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" className="w-4 h-4">
        <path d="M12 11v5" strokeLinecap="round" /><circle cx="12" cy="7.5" r="0.6" fill="currentColor" stroke="none" />
        <circle cx="12" cy="12" r="9" />
      </svg>
    ),
  },
};

export function useToast() {
  const [toasts, setToasts] = useState<ToastItem[]>([]);
  const counter = useRef(0);

  const dismiss = useCallback((id: number) => {
    setToasts((prev) => prev.filter((t) => t.id !== id));
  }, []);

  const show = useCallback((message: string, variant: ToastVariant = "success", title?: string) => {
    const id = ++counter.current;
    setToasts((prev) => [...prev, { id, message, variant, title }]);
    setTimeout(() => setToasts((prev) => prev.filter((t) => t.id !== id)), TOAST_DURATION);
  }, []);

  const node = (
    <div className="fixed top-5 right-4 sm:right-6 z-[100] flex flex-col items-end gap-3 pointer-events-none w-[calc(100%-2rem)] sm:w-auto max-w-sm">
      {toasts.map((t) => {
        const meta = TOAST_META[t.variant];
        return (
          <div
            key={t.id}
            role="status"
            aria-live="polite"
            className="villa-toast-in pointer-events-auto w-full overflow-hidden bg-white border border-mist rounded-xl shadow-card-hover"
          >
            <div className="flex items-start gap-3 pl-4 pr-3 py-3.5">
              <span className={`shrink-0 mt-0.5 flex items-center justify-center w-7 h-7 rounded-full text-white ${meta.ring}`}>
                {meta.icon}
              </span>
              <div className="flex-1 min-w-0 pt-0.5">
                <div className="font-mono text-[10px] uppercase tracking-widest text-ink-soft mb-0.5">
                  {t.title ?? meta.label}
                </div>
                <p className="text-sm font-medium text-forest-deep leading-snug break-words">{t.message}</p>
              </div>
              <button
                onClick={() => dismiss(t.id)}
                className="shrink-0 -mr-1 -mt-1 w-7 h-7 flex items-center justify-center rounded-full text-ink-soft hover:text-ink hover:bg-sage transition-colors text-lg leading-none"
                aria-label="Dismiss notification"
              >
                ×
              </button>
            </div>
            <div className="h-0.5 w-full bg-mist/50">
              <div
                className={`h-full origin-left ${meta.bar}`}
                style={{ animation: `villa-toast-progress ${TOAST_DURATION}ms linear forwards` }}
              />
            </div>
          </div>
        );
      })}
    </div>
  );

  return { show, node };
}

// ─────────────────────────────────────────────────────────────────────────────
// useConfirm
// ─────────────────────────────────────────────────────────────────────────────

export function useConfirm() {
  const [state, setState] = useState<{ open: boolean; opts: ConfirmOptions; resolve: (val: boolean) => void } | null>(null);

  const confirm = useCallback((opts: ConfirmOptions): Promise<boolean> => {
    return new Promise((resolve) => { setState({ open: true, opts, resolve }); });
  }, []);

  function handleAnswer(val: boolean) {
    state?.resolve(val);
    setState(null);
  }

  const node = state?.open ? (
    <div className="fixed inset-0 z-[95] flex items-center justify-center bg-ink/40 backdrop-blur-sm px-4">
      <div className="bg-white rounded-lg border border-mist shadow-card-hover w-full max-w-sm">
        <div className="px-6 pt-6 pb-4">
          <h3 className="font-display font-semibold text-lg text-forest-deep mb-2">{state.opts.title}</h3>
          <p className="text-sm text-ink-soft leading-relaxed">{state.opts.body}</p>
        </div>
        <div className="flex gap-3 justify-end px-6 pb-5">
          <button onClick={() => handleAnswer(false)} className="inline-flex items-center justify-center font-semibold text-sm px-5 py-2 rounded-pill border border-mist text-forest hover:bg-forest hover:border-forest hover:text-white transition-colors">Cancel</button>
          <button onClick={() => handleAnswer(true)} className={`inline-flex items-center justify-center font-semibold text-sm px-5 py-2 rounded-pill transition-colors ${state.opts.danger ? "bg-clay text-white hover:opacity-90" : "bg-forest text-white hover:bg-forest-deep"}`}>
            {state.opts.confirmLabel ?? "Confirm"}
          </button>
        </div>
      </div>
    </div>
  ) : null;

  return { confirm, node };
}

// ─────────────────────────────────────────────────────────────────────────────
// Emoji picker data
// ─────────────────────────────────────────────────────────────────────────────

const EMOJI_CATEGORIES: { label: string; icon: string; emojis: string[] }[] = [
  {
    label: "Smileys",
    icon: "😊",
    emojis: ["😀","😃","😄","😁","😆","😅","🤣","😂","🙂","😊","😇","🥰","😍","🤩","😘","😗","😚","😙","🥲","😋","😛","😜","🤪","😝","🤑","🤗","🤭","🤫","🤔","🤐","🥴","😐","😑","😶","😏","😒","🙄","😬","🤥","😌","😔","😪","🤤","😴","😷","🤒","🤕","🤢","🤮","🤧","🥵","🥶","😵","🤯","🤠","🥳","🥸","😎","🤓","🧐","😕","😟","🙁","☹️","😮","😯","😲","😳","🥺","😦","😧","😨","😰","😥","😢","😭","😱","😖","😣","😞","😓","😩","😫","🥱","😤","😠","😡","🤬","😈","👿"],
  },
  {
    label: "Gestures",
    icon: "👋",
    emojis: ["👋","🤚","🖐️","✋","🖖","👌","🤌","🤏","✌️","🤞","🤟","🤘","🤙","👈","👉","👆","🖕","👇","☝️","👍","👎","✊","👊","🤛","🤜","👏","🙌","👐","🤲","🤝","🙏","✍️","💅","🤳","💪","🦾","🦵","🦶","👂","🦻","👃","🫀","🫁","🧠","🦷","🦴","👀","👁️","👅","👄","🫦","💋","💘","💝","💖","💗","💓","💞","💕","💟","❣️","💔","❤️","🧡","💛","💚","💙","💜","🖤","🤍","🤎"],
  },
  {
    label: "People",
    icon: "👤",
    emojis: ["👶","🧒","👦","👧","🧑","👱","👨","🧔","👩","🧓","👴","👵","🙍","🙎","🙅","🙆","💁","🙋","🧏","🙇","🤦","🤷","👮","🕵️","💂","🥷","👷","🫅","🤴","👸","👳","👲","🧕","🤵","👰","🤰","🫃","🤱","👼","🎅","🤶","🦸","🦹","🧙","🧝","🧛","🧟","🧌","🧞","🧜","🧚","🧑‍🎄","🫶","🧑‍🤝‍🧑","👫","👬","👭","💏","💑","👪"],
  },
  {
    label: "Nature",
    icon: "🌿",
    emojis: ["🐶","🐱","🐭","🐹","🐰","🦊","🐻","🐼","🐨","🐯","🦁","🐮","🐷","🐸","🐵","🙈","🙉","🙊","🐒","🐔","🐧","🐦","🐤","🦆","🦅","🦉","🦇","🐺","🐗","🐴","🦄","🐝","🪱","🐛","🦋","🐌","🐞","🐜","🪲","🦟","🦗","🪳","🕷️","🦂","🐢","🐍","🦎","🦖","🦕","🐙","🦑","🦐","🦞","🦀","🐡","🐠","🐟","🐬","🐳","🐋","🦈","🐊","🌸","🌺","🌻","🌹","🌷","🌲","🌳","🌴","🌵","🌾","☘️","🍀","🍁","🍂","🍃","🪴","🌞","🌝","🌛","🌜","🌙","⭐","🌟","💫","⚡","🔥","💧","🌊","🌈","❄️","☃️","⛄"],
  },
  {
    label: "Food",
    icon: "🍕",
    emojis: ["🍎","🍊","🍋","🍇","🍓","🫐","🍈","🍒","🍑","🥭","🍍","🥥","🥝","🍅","🍆","🥑","🥦","🥬","🧅","🥕","🌽","🌶️","🫑","🥒","🥗","🥘","🍲","🫕","🥣","🧆","🥚","🍳","🫔","🌮","🌯","🫙","🥫","🍱","🍘","🍙","🍚","🍛","🍜","🍝","🍠","🍢","🍣","🍤","🍥","🥮","🍡","🧁","🍰","🎂","🍮","🍭","🍬","🍫","🍿","🍩","🍪","🌰","🥜","🍯","🧈","🥞","🧇","🥓","🥩","🍗","🍖","🍔","🌭","🍟","🍕","🧀","🥨","🥐","🥖","🫓","🥯","🧋","🍵","☕","🍺","🍻","🥂","🍷","🥃","🍸","🍹"],
  },
  {
    label: "Activities",
    icon: "⚽",
    emojis: ["⚽","🏀","🏈","⚾","🥎","🎾","🏐","🏉","🥏","🎱","🪀","🏓","🏸","🏒","🥅","🎿","🛷","🥌","🎯","🎣","🤿","🎽","🎪","🤹","🎭","🎨","🎬","🎤","🎧","🎼","🎵","🎶","🎹","🥁","🎷","🎺","🎸","🪕","🎻","🪗","🎲","♟️","🎮","🕹️","🎰","🧩","🪆","🪅","🎈","🎉","🎊","🎁","🎀","🎗️","🎟️","🎫","🏆","🥇","🥈","🥉","🏅","🎖️","🏵️"],
  },
  {
    label: "Travel",
    icon: "✈️",
    emojis: ["🚗","🚕","🚙","🚌","🚎","🏎️","🚓","🚑","🚒","🚐","🛻","🚚","🚛","🚜","🏍️","🛵","🚲","🛴","🛺","🚁","🛸","✈️","🛩️","🚀","🛶","⛵","🚢","🚂","🚃","🚄","🚅","🚆","🚇","🚈","🚉","🚊","🚝","🚞","🚋","🚌","🚍","🗺️","🧭","⛰️","🌋","🗻","🏕️","🏖️","🏜️","🏝️","🏞️","🏟️","🏛️","🏗️","🏘️","🏙️","🏚️","🏠","🏡","🏢","🏣","🏤","🏥","🏦","🏨","🏩","🏪","🏫","🏬","🏭","🏯","🏰","💒","🗼","🗽","🗾","⛪","🕌","🕍","⛩️","🕋"],
  },
  {
    label: "Objects",
    icon: "💡",
    emojis: ["⌚","📱","💻","⌨️","🖥️","🖨️","🖱️","🖲️","💾","💿","📀","📷","📸","📹","🎥","📽️","🎞️","📞","☎️","📟","📺","📻","🧭","⏱️","⏲️","⏰","🕰️","⌛","⏳","📡","🔋","🪫","🔌","💡","🔦","🕯️","🪔","🧯","💰","💴","💵","💶","💷","💸","💳","🪙","💹","📈","📉","📊","💼","🗂️","🗃️","🗄️","🗑️","🔒","🔓","🔏","🔐","🔑","🗝️","🔨","🪓","⛏️","⚒️","🛠️","🗡️","⚔️","🛡️","🔧","🔩","⚙️","🗜️","⚖️","🦯","🔗","⛓️","🪝","🧰","🪜","🧲","🪜","💊","🩺","🌡️","🩻","🩹","🪞","🛁","🪠","🧴","🧷","🧹","🧺","🧻","🧼","🪥","🪣","🧽","🪒","🛒","🚪","🪑","🚿","🛋️","🪞"],
  },
  {
    label: "Symbols",
    icon: "❤️",
    emojis: ["❤️","🧡","💛","💚","💙","💜","🖤","🤍","🤎","💔","❣️","💕","💞","💓","💗","💖","💘","💝","💟","☮️","✝️","☪️","🕉️","☸️","✡️","🔯","🕎","☯️","☦️","⛎","♈","♉","♊","♋","♌","♍","♎","♏","♐","♑","♒","♓","🆔","⚛️","🉑","☢️","☣️","📴","📳","🈶","🈚","🈸","🈺","🈷️","✴️","🆚","💮","🉐","㊙️","㊗️","🈴","🈵","🈹","🈲","🅰️","🅱️","🆎","🆑","🅾️","🆘","❌","⭕","🛑","⛔","📛","🚫","💯","❗","❕","❓","❔","‼️","⁉️","🔅","🔆","〽️","⚠️","🚸","🔱","⚜️","🔰","♻️","✅","🈯","💹","❎","🌐","💠","Ⓜ️","🌀","💤","🏧","🚾","♿","🅿️","🛗","🈳","🈹","🚰","🚹","🚺","🚻","🚼","🚽","⚠️","🔞","📵","🔕","🎵","🎶","✔️","🔀","🔁","🔂","▶️","⏩","⏭️","⏯️","◀️","⏪","⏮️","🔼","⏫","🔽","⏬","⏸️","⏹️","⏺️","🎦","🔅","🔆","📶","📳","📴","🔇","🔈","🔉","🔊","📢","📣","🔔","🔕","🃏","🀄","🎴","🔮"],
  },
];

const RECENTLY_USED_KEY = "villa_recent_emojis";
const MAX_RECENT = 24;

function getRecentEmojis(): string[] {
  try { return JSON.parse(localStorage.getItem(RECENTLY_USED_KEY) ?? "[]"); }
  catch { return []; }
}

function addRecentEmoji(emoji: string) {
  try {
    const prev = getRecentEmojis().filter((e) => e !== emoji);
    localStorage.setItem(RECENTLY_USED_KEY, JSON.stringify([emoji, ...prev].slice(0, MAX_RECENT)));
  } catch { /* ignore */ }
}

// ─────────────────────────────────────────────────────────────────────────────
// EmojiPicker component
// ─────────────────────────────────────────────────────────────────────────────

interface EmojiPickerProps {
  onSelect: (emoji: string) => void;
  onClose: () => void;
}

function EmojiPicker({ onSelect, onClose }: EmojiPickerProps) {
  const [query, setQuery] = useState("");
  const [activeCategory, setActiveCategory] = useState(0);
  const [recents, setRecents] = useState<string[]>([]);
  const pickerRef = useRef<HTMLDivElement>(null);
  const searchRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    setRecents(getRecentEmojis());
    setTimeout(() => searchRef.current?.focus(), 50);
  }, []);

  // Close on outside click
  useEffect(() => {
    function handleClick(e: MouseEvent) {
      if (pickerRef.current && !pickerRef.current.contains(e.target as Node)) onClose();
    }
    document.addEventListener("mousedown", handleClick);
    return () => document.removeEventListener("mousedown", handleClick);
  }, [onClose]);

  function handleSelect(emoji: string) {
    addRecentEmoji(emoji);
    setRecents(getRecentEmojis());
    onSelect(emoji);
  }

  const searchResults = query.trim()
    ? EMOJI_CATEGORIES.flatMap((c) => c.emojis).filter((e) => {
        const lower = query.toLowerCase();
        // very simple: filter emojis by trying to match against category name + index heuristic
        return true; // show all when searching — user relies on visual scan
      }).slice(0, 60)
    : [];

  // For search, return all emojis that visually match — since we can't do text search without
  // a full emoji dataset, we just show every emoji but filter categories
  const filteredCategories = query.trim()
    ? [{ label: `Results for "${query}"`, icon: "🔍", emojis: EMOJI_CATEGORIES.flatMap((c) => c.emojis) }]
    : activeCategory === -1
    ? [{ label: "Recently used", icon: "🕐", emojis: recents }]
    : [EMOJI_CATEGORIES[activeCategory]];

  const allTabs = [
    ...(recents.length > 0 ? [{ icon: "🕐", label: "Recent", idx: -1 }] : []),
    ...EMOJI_CATEGORIES.map((c, i) => ({ icon: c.icon, label: c.label, idx: i })),
  ];

  return (
    <div
      ref={pickerRef}
      className="absolute bottom-full mb-2 right-0 z-50 bg-white border border-mist rounded-xl shadow-card-hover w-[320px] overflow-hidden"
      style={{ maxHeight: 380 }}
    >
      {/* Search */}
      <div className="px-3 pt-3 pb-2 border-b border-mist">
        <div className="flex items-center gap-2 bg-sage border border-mist rounded-lg px-3 py-2">
          <svg className="w-3.5 h-3.5 text-ink-soft shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
            <circle cx="11" cy="11" r="7" /><path d="M21 21l-4.35-4.35" strokeLinecap="round" />
          </svg>
          <input
            ref={searchRef}
            value={query}
            onChange={(e) => setQuery(e.target.value)}
            placeholder="Search emoji…"
            className="flex-1 bg-transparent border-none outline-none text-sm text-ink placeholder:text-ink-soft"
          />
          {query && (
            <button onClick={() => setQuery("")} className="text-ink-soft hover:text-ink text-base leading-none">×</button>
          )}
        </div>
      </div>

      {/* Category tabs */}
      {!query && (
        <div className="flex overflow-x-auto border-b border-mist bg-white px-1 pt-1 gap-0.5" style={{ scrollbarWidth: "none" }}>
          {allTabs.map(({ icon, label, idx }) => (
            <button
              key={idx}
              title={label}
              onClick={() => setActiveCategory(idx)}
              className={`flex-shrink-0 w-8 h-8 rounded-md flex items-center justify-center text-base transition-colors ${
                activeCategory === idx ? "bg-sage" : "hover:bg-sage/60"
              }`}
            >
              {icon}
            </button>
          ))}
        </div>
      )}

      {/* Category label */}
      {!query && (
        <div className="px-3 py-1.5">
          <span className="font-mono text-[10px] uppercase tracking-widest text-ink-soft">
            {activeCategory === -1 ? "Recently used" : EMOJI_CATEGORIES[activeCategory]?.label}
          </span>
        </div>
      )}

      {/* Emoji grid */}
      <div className="overflow-y-auto px-2 pb-3" style={{ maxHeight: 240 }}>
        {filteredCategories.map((cat) =>
          cat.emojis.length === 0 ? (
            <div key="empty" className="text-center py-6 text-sm text-ink-soft">No recent emojis yet</div>
          ) : (
            <div key={cat.label} className="grid grid-cols-8 gap-0.5">
              {cat.emojis.map((emoji, i) => (
                <button
                  key={`${emoji}-${i}`}
                  onClick={() => handleSelect(emoji)}
                  className="w-9 h-9 flex items-center justify-center text-xl rounded-md hover:bg-sage transition-colors"
                  title={emoji}
                >
                  {emoji}
                </button>
              ))}
            </div>
          )
        )}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// MessageThread — full messaging UI with emoji picker
// ─────────────────────────────────────────────────────────────────────────────

interface MessageThreadProps {
  myName?: string; // optional; the API already projects sender as "me"/"them"
}

function Avatar({ initials, size = "md" }: { initials: string; size?: "sm" | "md" | "lg" }) {
  const sz = { sm: "w-8 h-8 text-xs", md: "w-10 h-10 text-sm", lg: "w-12 h-12 text-base" };
  return (
    <div className={`rounded-full bg-mist flex items-center justify-center text-forest font-semibold shrink-0 ${sz[size]}`}>
      {initials}
    </div>
  );
}

export function MessageThread(_props: MessageThreadProps = {}) {
  const [conversations, setConversations] = useState<Conversation[]>([]);
  const [loadingList, setLoadingList] = useState(true);
  const [activeId, setActiveId] = useState<string | null>(null);
  const [draft, setDraft] = useState("");
  const [search, setSearch] = useState("");
  const [typing] = useState(false);
  const [showEmoji, setShowEmoji] = useState(false);
  const bottomRef = useRef<HTMLDivElement>(null);
  const inputRef = useRef<HTMLTextAreaElement>(null);

  const active = conversations.find((c) => c.id === activeId) ?? null;

  // Load the inbox from the API on mount.
  useEffect(() => {
    (async () => {
      try {
        const res = await clientExecute<{ items: Array<{ id: string; with: string; role: string; avatar: string; propertyTitle?: string; propertyAddress?: string; unread: number; lastMessage?: string; lastTime?: string }> }>("message.list-conversations", { limit: 100 });
        setConversations(res.items.map((c) => ({
          id: c.id, with: c.with, role: c.role, avatar: c.avatar,
          propertyTitle: c.propertyTitle ?? "", propertyAddress: c.propertyAddress ?? "",
          unread: (c.unread ?? 0) > 0, lastMessage: c.lastMessage ?? "", lastTime: fmtTime(c.lastTime),
          messages: [],
        })));
      } catch { /* ignore */ } finally { setLoadingList(false); }
    })();
  }, []);

  useEffect(() => {
    setTimeout(() => bottomRef.current?.scrollIntoView({ behavior: "smooth" }), 50);
  }, [activeId, active?.messages.length]);

  // close emoji picker on Escape
  useEffect(() => {
    function handleKey(e: KeyboardEvent) {
      if (e.key === "Escape") setShowEmoji(false);
    }
    document.addEventListener("keydown", handleKey);
    return () => document.removeEventListener("keydown", handleKey);
  }, []);

  async function openConversation(id: string) {
    setActiveId(id);
    setDraft("");
    setShowEmoji(false);
    setConversations((prev) => prev.map((c) => (c.id === id ? { ...c, unread: false } : c)));
    setTimeout(() => inputRef.current?.focus(), 100);
    try {
      const thread = await clientExecute<{ messages: Array<{ id: string; sender: "me" | "them"; text: string; time?: string }> }>("message.get-thread", { conversationId: id });
      setConversations((prev) => prev.map((c) => c.id === id
        ? { ...c, messages: thread.messages.map((m) => ({ id: m.id, sender: m.sender, text: m.text, time: fmtTime(m.time) })) }
        : c));
      clientExecute("message.mark-read", { conversationId: id }).catch(() => { /* ignore */ });
    } catch { /* ignore */ }
  }

  function insertEmoji(emoji: string) {
    const ta = inputRef.current;
    if (!ta) {
      setDraft((d) => d + emoji);
      return;
    }
    const start = ta.selectionStart ?? draft.length;
    const end = ta.selectionEnd ?? draft.length;
    const next = draft.slice(0, start) + emoji + draft.slice(end);
    setDraft(next);
    // restore cursor after emoji
    requestAnimationFrame(() => {
      ta.focus();
      const pos = start + emoji.length;
      ta.setSelectionRange(pos, pos);
      // resize
      ta.style.height = "auto";
      ta.style.height = `${Math.min(ta.scrollHeight, 120)}px`;
    });
  }

  async function sendMessage() {
    if (!draft.trim() || !activeId) return;
    const text = draft.trim();
    const optimistic: ChatMessage = { id: `tmp-${Date.now()}`, sender: "me", text, time: fmtTime(new Date()) };
    setConversations((prev) => prev.map((c) =>
      c.id === activeId
        ? { ...c, messages: [...c.messages, optimistic], lastMessage: text, lastTime: "Just now", unread: false }
        : c
    ));
    setDraft("");
    setShowEmoji(false);
    if (inputRef.current) inputRef.current.style.height = "auto";
    try {
      await clientExecute("message.reply", { conversationId: activeId, text });
    } catch { /* keep optimistic message; a retry/failed state could be added */ }
  }

  function handleKey(e: React.KeyboardEvent<HTMLTextAreaElement>) {
    if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); sendMessage(); }
  }

  function handleInput(e: React.FormEvent<HTMLTextAreaElement>) {
    const t = e.currentTarget;
    t.style.height = "auto";
    t.style.height = `${Math.min(t.scrollHeight, 120)}px`;
  }

  const filtered = conversations.filter(
    (c) => !search || c.with.toLowerCase().includes(search.toLowerCase()) || c.propertyTitle.toLowerCase().includes(search.toLowerCase())
  );
  const unreadCount = conversations.filter((c) => c.unread).length;

  return (
    <div className="bg-white border border-mist rounded-md overflow-hidden">
      <div className="grid grid-cols-1 md:grid-cols-[300px_1fr]" style={{ minHeight: 540 }}>

        {/* ── Conversation list ── */}
        <div className={`border-r border-mist flex flex-col ${activeId ? "hidden md:flex" : "flex"}`} style={{ minHeight: 540 }}>
          <div className="px-4 py-4 border-b border-mist shrink-0">
            <div className="flex items-center justify-between mb-3">
              <h3 className="font-display font-semibold text-lg text-forest-deep">Inbox</h3>
              {unreadCount > 0 && (
                <span className="inline-flex items-center justify-center min-w-[20px] h-5 rounded-full bg-gold text-forest-deep text-[11px] font-bold px-1">{unreadCount}</span>
              )}
            </div>
            <div className="flex items-center gap-2 bg-sage border border-mist rounded-pill px-3 py-2">
              <svg className="w-4 h-4 text-ink-soft shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
                <circle cx="11" cy="11" r="7" /><path d="M21 21l-4.35-4.35" strokeLinecap="round" />
              </svg>
              <input value={search} onChange={(e) => setSearch(e.target.value)} placeholder="Search conversations…"
                className="flex-1 bg-transparent border-none outline-none text-sm text-ink placeholder:text-ink-soft" />
              {search && <button onClick={() => setSearch("")} className="text-ink-soft hover:text-ink text-base leading-none">×</button>}
            </div>
          </div>

          <div className="flex-1 overflow-y-auto divide-y divide-mist">
            {loadingList
              ? <InlineLoader label="Loading conversations" />
              : filtered.length === 0 && <div className="text-center py-10 text-sm text-ink-soft px-4">No conversations yet.</div>}
            {filtered.map((c) => (
              <button key={c.id} onClick={() => openConversation(c.id)}
                className={`w-full text-left px-4 py-3.5 flex gap-3 items-start transition-colors hover:bg-sage/60 ${activeId === c.id ? "bg-[#E8F2EC]" : c.unread ? "bg-[#FFFDF6]" : ""}`}>
                <div className="relative shrink-0 mt-0.5">
                  <Avatar initials={c.avatar} size="md" />
                  {c.unread && <span className="absolute -top-0.5 -right-0.5 w-2.5 h-2.5 rounded-full bg-gold border-2 border-white" />}
                </div>
                <div className="flex-1 min-w-0">
                  <div className="flex justify-between items-baseline gap-1 mb-0.5">
                    <span className={`text-sm truncate ${c.unread ? "font-semibold text-forest-deep" : "font-medium text-ink"}`}>{c.with}</span>
                    <span className="text-[11px] text-ink-soft whitespace-nowrap shrink-0">{c.lastTime}</span>
                  </div>
                  {c.propertyTitle && <div className="font-mono text-[10px] uppercase tracking-wider text-gold-deep mb-0.5 truncate">{c.propertyTitle}</div>}
                  <p className={`text-xs truncate ${c.unread ? "font-medium text-ink" : "text-ink-soft"}`}>{c.lastMessage}</p>
                </div>
              </button>
            ))}
          </div>
        </div>

        {/* ── Chat pane ── */}
        <div className={`flex flex-col ${!activeId ? "hidden md:flex" : "flex"}`} style={{ minHeight: 540 }}>
          {!active ? (
            <div className="flex-1 flex flex-col items-center justify-center text-ink-soft gap-3 p-8">
              <svg className="w-14 h-14 text-mist" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.2">
                <path d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2v10z" strokeLinejoin="round" />
              </svg>
              <p className="text-sm text-center max-w-[22ch] text-ink-soft">Select a conversation to read and reply to messages</p>
            </div>
          ) : (
            <>
              {/* Chat header */}
              <div className="flex items-center gap-3 px-5 py-3.5 border-b border-mist shrink-0 bg-white">
                <button onClick={() => { setActiveId(null); setShowEmoji(false); }}
                  className="md:hidden flex items-center justify-center w-8 h-8 rounded-full text-forest hover:bg-sage transition-colors" aria-label="Back">
                  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" className="w-5 h-5">
                    <path d="M15 18l-6-6 6-6" strokeLinecap="round" strokeLinejoin="round" />
                  </svg>
                </button>
                <Avatar initials={active.avatar} size="sm" />
                <div className="flex-1 min-w-0">
                  <div className="font-semibold text-sm text-forest-deep">{active.with}</div>
                  <div className="text-xs text-ink-soft truncate">
                    <span className="capitalize">{active.role}</span>
                    {active.propertyTitle && <> · <span className="text-gold-deep">{active.propertyTitle}</span></>}
                  </div>
                </div>
              </div>

              {/* Messages */}
              <div className="flex-1 overflow-y-auto px-5 py-4 flex flex-col gap-3" style={{ minHeight: 0, maxHeight: 400 }}>
                <div className="flex items-center gap-3 my-1">
                  <div className="flex-1 h-px bg-mist" />
                  <span className="text-[11px] font-mono text-ink-soft uppercase tracking-wider">Today</span>
                  <div className="flex-1 h-px bg-mist" />
                </div>
                {active.messages.map((msg, i) => {
                  const prevSender = i > 0 ? active.messages[i - 1].sender : null;
                  const isFirst = prevSender !== msg.sender;
                  return (
                    <div key={msg.id} className={`flex gap-2 ${msg.sender === "me" ? "flex-row-reverse" : "flex-row"} ${isFirst ? "mt-2" : ""}`}>
                      {msg.sender === "them" && (
                        <div className={isFirst ? "opacity-100" : "opacity-0 pointer-events-none"}>
                          <Avatar initials={active.avatar} size="sm" />
                        </div>
                      )}
                      <div className={`flex flex-col gap-0.5 max-w-[72%] ${msg.sender === "me" ? "items-end" : "items-start"}`}>
                        <div className={`text-sm px-4 py-2.5 leading-relaxed break-words ${msg.sender === "me" ? "bg-forest text-white rounded-2xl rounded-tr-sm" : "bg-sage border border-mist text-ink rounded-2xl rounded-tl-sm"}`}>
                          {msg.text}
                        </div>
                        <span className="text-[11px] text-ink-soft px-1">{msg.time}</span>
                      </div>
                    </div>
                  );
                })}
                {typing && (
                  <div className="flex gap-2 items-end mt-1">
                    <Avatar initials={active.avatar} size="sm" />
                    <div className="bg-sage border border-mist rounded-2xl rounded-tl-sm px-4 py-3 flex gap-1 items-center">
                      <span className="w-1.5 h-1.5 rounded-full bg-ink-soft animate-bounce" style={{ animationDelay: "0ms" }} />
                      <span className="w-1.5 h-1.5 rounded-full bg-ink-soft animate-bounce" style={{ animationDelay: "150ms" }} />
                      <span className="w-1.5 h-1.5 rounded-full bg-ink-soft animate-bounce" style={{ animationDelay: "300ms" }} />
                    </div>
                  </div>
                )}
                <div ref={bottomRef} />
              </div>

              {/* Input area */}
              <div className="border-t border-mist px-4 py-3 flex gap-2 items-end shrink-0 bg-white relative">
                {/* Emoji picker */}
                {showEmoji && (
                  <EmojiPicker
                    onSelect={(emoji) => { insertEmoji(emoji); }}
                    onClose={() => setShowEmoji(false)}
                  />
                )}

                {/* Emoji button */}
                <button
                  onClick={() => setShowEmoji((s) => !s)}
                  className={`flex items-center justify-center w-9 h-9 rounded-full transition-colors shrink-0 text-xl ${showEmoji ? "bg-sage text-forest" : "text-ink-soft hover:bg-sage hover:text-forest"}`}
                  aria-label="Emoji picker"
                  title="Add emoji"
                >
                  😊
                </button>

                {/* Text input */}
                <textarea
                  ref={inputRef}
                  value={draft}
                  onChange={(e) => setDraft(e.target.value)}
                  onKeyDown={handleKey}
                  onInput={handleInput}
                  placeholder="Write a message… (Enter to send)"
                  rows={1}
                  className="flex-1 resize-none border border-mist rounded-xl px-4 py-2.5 text-sm text-ink bg-white focus:outline-2 focus:outline-gold focus:border-forest placeholder:text-ink-soft transition-colors"
                  style={{ minHeight: 44, maxHeight: 120 }}
                />

                {/* Send button */}
                <button
                  onClick={sendMessage}
                  disabled={!draft.trim()}
                  className="inline-flex items-center justify-center w-10 h-10 rounded-full bg-forest text-white hover:bg-forest-deep transition-colors disabled:opacity-40 shrink-0"
                  aria-label="Send message"
                >
                  <svg viewBox="0 0 24 24" fill="currentColor" className="w-4 h-4">
                    <path d="M3.4 2.7a.75.75 0 00-.954 1.04l2.6 5.26H14a.75.75 0 010 1.5H5.046l-2.6 5.26a.75.75 0 00.953 1.04l18-8.5a.75.75 0 000-1.36l-18-5.24z" />
                  </svg>
                </button>
              </div>
            </>
          )}
        </div>
      </div>
    </div>
  );
}
