import { cn } from "@xtablo/shared";
import {
  AlertCircle,
  ArrowUp,
  Check,
  CheckCheck,
  ChevronDown,
  Clock,
  Mic,
  MoreHorizontal,
  Paperclip,
  Pause,
  Pencil,
  Pin,
  // Plus,
  Play,
  Reply,
  SmilePlus,
  Trash2,
  // Image as ImageIcon,
  // Smile,
  Upload,
  X,
} from "lucide-react";
import * as React from "react";
import { createPortal } from "react-dom";
import {
  formatTimestamp,
  groupMessages,
  useAutoResize,
  useAutoScroll,
  useTypingIndicator,
} from "../hooks";
import type {
  ChatConfig,
  ChatLabels,
  ChatMessageData,
  ChatUser,
  MessageGroup,
  TypingUser,
} from "../types";

// ─── Context ──────────────────────────────────────────────────────────────────

const ChatContext = React.createContext<ChatConfig | null>(null);

function useChatContext() {
  const ctx = React.useContext(ChatContext);
  if (!ctx) throw new Error("Chat components must be wrapped in <ChatProvider>");
  return ctx;
}

// ─── ChatProvider ─────────────────────────────────────────────────────────────

interface ChatProviderProps {
  currentUser: ChatUser;
  dateFormat?: "relative" | "absolute" | "time-only";
  messageGroupingInterval?: number;
  labels?: ChatLabels;
  onReactionAdd?: (messageId: string, emoji: string) => void;
  onReactionRemove?: (messageId: string, emoji: string) => void;
  onReply?: (message: ChatMessageData) => void;
  onEdit?: (message: ChatMessageData) => void;
  onDelete?: (messageId: string) => void;
  onPin?: (messageId: string) => void;
  children: React.ReactNode;
  style?: React.CSSProperties;
  className?: string;
}

function ChatProvider({
  currentUser,
  dateFormat = "relative",
  messageGroupingInterval = 120,
  labels,
  onReactionAdd,
  onReactionRemove,
  onReply,
  onEdit,
  onDelete,
  onPin,
  children,
  style,
  className,
}: ChatProviderProps) {
  const config = React.useMemo<ChatConfig>(
    () => ({
      currentUser,
      dateFormat,
      messageGroupingInterval,
      labels,
      onReactionAdd,
      onReactionRemove,
      onReply,
      onEdit,
      onDelete,
      onPin,
    }),
    [
      currentUser,
      dateFormat,
      messageGroupingInterval,
      labels,
      onReactionAdd,
      onReactionRemove,
      onReply,
      onEdit,
      onDelete,
      onPin,
    ]
  );

  return (
    <ChatContext.Provider value={config}>
      <div style={style} className={className}>
        {children}
      </div>
    </ChatContext.Provider>
  );
}

// ─── Quick emoji picker (6 common reactions) ──────────────────────────────────

const QUICK_REACTIONS = [
  "\u{1F44D}",
  "\u{2764}\u{FE0F}",
  "\u{1F602}",
  "\u{1F62E}",
  "\u{1F64F}",
  "\u{1F525}",
];

function QuickReactionPicker({
  onSelect,
  onClose,
}: {
  onSelect: (emoji: string) => void;
  onClose: () => void;
}) {
  return (
    <div
      className="chat-toolbar-enter flex items-center gap-0.5 rounded-[10px] border border-border bg-card p-1 shadow-md"
      onMouseLeave={onClose}
    >
      {QUICK_REACTIONS.map((emoji) => (
        <button
          key={emoji}
          onClick={() => {
            onSelect(emoji);
            onClose();
          }}
          className="flex size-8 items-center justify-center rounded-lg text-lg transition-transform hover:scale-125 hover:bg-accent"
          aria-label={`React with ${emoji}`}
        >
          {emoji}
        </button>
      ))}
    </div>
  );
}

// ─── ChatMessageActions (hover toolbar) ───────────────────────────────────────

interface ChatMessageActionsProps {
  message: ChatMessageData;
  isOutgoing: boolean;
}

function ChatMessageActions({ message, isOutgoing }: ChatMessageActionsProps) {
  const { onReply, onReactionAdd, onEdit, onDelete, onPin } = useChatContext();
  const [showReactions, setShowReactions] = React.useState(false);
  const [showMore, setShowMore] = React.useState(false);

  return (
    <div
      className={cn(
        "chat-toolbar-enter absolute -top-3 z-10 flex items-center gap-0.5 rounded-lg border border-border bg-card p-0.5 opacity-0 shadow-md transition-opacity group-hover/message:opacity-100",
        isOutgoing ? "right-0" : "left-10"
      )}
    >
      {/* Reply */}
      <button
        onClick={() => onReply?.(message)}
        className="flex size-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
        aria-label="Reply"
      >
        <Reply className="size-3.5" />
      </button>

      {/* React — opens quick picker */}
      <div className="relative">
        <button
          onClick={() => setShowReactions(!showReactions)}
          className="flex size-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
          aria-label="Add reaction"
        >
          <SmilePlus className="size-3.5" />
        </button>
        {showReactions && (
          <div className="absolute bottom-full left-1/2 z-20 mb-2 -translate-x-1/2">
            <QuickReactionPicker
              onSelect={(emoji) => onReactionAdd?.(message.id, emoji)}
              onClose={() => setShowReactions(false)}
            />
          </div>
        )}
      </div>

      {/* More — dropdown */}
      <div className="relative">
        <button
          onClick={() => setShowMore(!showMore)}
          className="flex size-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
          aria-label="More actions"
        >
          <MoreHorizontal className="size-3.5" />
        </button>
        {showMore && (
          <div
            className={cn(
              "chat-toolbar-enter absolute top-full z-20 mt-1 w-40 overflow-hidden rounded-lg border border-border bg-card py-1 shadow-md",
              isOutgoing ? "right-0" : "left-0"
            )}
            onMouseLeave={() => setShowMore(false)}
          >
            {isOutgoing && (
              <button
                onClick={() => {
                  onEdit?.(message);
                  setShowMore(false);
                }}
                className="flex w-full items-center gap-2 px-3 py-1.5 text-[13px] text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
              >
                <Pencil className="size-3.5" />
                Edit
              </button>
            )}
            <button
              onClick={() => {
                onPin?.(message.id);
                setShowMore(false);
              }}
              className="flex w-full items-center gap-2 px-3 py-1.5 text-[13px] text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
            >
              <Pin className="size-3.5" />
              {message.isPinned ? "Unpin" : "Pin"}
            </button>
            {isOutgoing && (
              <button
                onClick={() => {
                  onDelete?.(message.id);
                  setShowMore(false);
                }}
                className="flex w-full items-center gap-2 px-3 py-1.5 text-[13px] text-destructive transition-colors hover:bg-red-500/10"
              >
                <Trash2 className="size-3.5" />
                Delete
              </button>
            )}
          </div>
        )}
      </div>
    </div>
  );
}

// ─── ChatMessageReply (quoted reply inside bubble) ────────────────────────────

function ChatMessageReply({
  replyTo,
  isOutgoing,
}: {
  replyTo: NonNullable<ChatMessageData["replyTo"]>;
  isOutgoing: boolean;
}) {
  // Outgoing bubbles set text color via --chat-bubble-outgoing-text which may
  // be white (Lunar, Midnight) or dark (Aurora, Ember).  Using `text-inherit`
  // + opacity lets the reply quote inherit that color and stay visible against
  // the bubble background regardless of theme.
  return (
    <div
      className={cn(
        "mb-1.5 flex items-start gap-2 rounded-lg border-l-2 px-2.5 py-1.5",
        isOutgoing
          ? "border-primary-foreground/30 bg-primary-foreground/10"
          : "border-primary bg-accent"
      )}
    >
      <div className="min-w-0 flex-1">
        <span
          className={cn(
            "block text-[12px] font-semibold",
            isOutgoing ? "text-inherit opacity-80" : "text-primary"
          )}
        >
          {replyTo.senderName}
        </span>
        <span
          className={cn(
            "block truncate text-[12px]",
            isOutgoing ? "text-inherit opacity-60" : "text-muted-foreground"
          )}
        >
          {replyTo.text}
        </span>
      </div>
    </div>
  );
}

// ─── ChatMessage ──────────────────────────────────────────────────────────────

interface ChatMessageProps {
  message: ChatMessageData;
  isOutgoing: boolean;
  position: "solo" | "first" | "middle" | "last";
  showSender?: boolean;
  showAvatar?: boolean;
  className?: string;
}

// ─── Voice Message ─────────────────────────────────────────────────────────

function ChatVoiceMessage({
  voice,
  isOutgoing,
}: {
  voice: NonNullable<ChatMessageData["voice"]>;
  isOutgoing: boolean;
}) {
  const [playing, setPlaying] = React.useState(false);
  const [progress, setProgress] = React.useState(0);
  const progressRef = React.useRef(0);

  React.useEffect(() => {
    progressRef.current = progress;
  }, [progress]);

  const totalMins = Math.floor(voice.duration / 60);
  const totalSecs = Math.floor(voice.duration % 60);
  const elapsed = progress * voice.duration;
  const elapsedMins = Math.floor(elapsed / 60);
  const elapsedSecs = Math.floor(elapsed % 60);
  const timeLabel =
    playing || progress > 0
      ? `${elapsedMins}:${elapsedSecs.toString().padStart(2, "0")}`
      : `${totalMins}:${totalSecs.toString().padStart(2, "0")}`;

  const progressIndex = Math.floor(progress * voice.waveform.length);

  React.useEffect(() => {
    if (!playing) return;
    const fps = 20;
    const step = 1 / (voice.duration * fps);
    const id = setInterval(() => {
      const next = progressRef.current + step;
      if (next >= 1) {
        setProgress(0);
        setPlaying(false);
        clearInterval(id);
      } else {
        setProgress(next);
      }
    }, 1000 / fps);
    return () => clearInterval(id);
  }, [playing, voice.duration]);

  const toggle = () => {
    if (!playing && progress === 0) setProgress(0);
    setPlaying((p) => !p);
  };

  return (
    <div className="mt-1.5 flex items-center gap-3">
      <button
        onClick={toggle}
        className="flex w-9 h-9 shrink-0 items-center justify-center rounded-full transition-colors"
        style={{ background: isOutgoing ? "rgba(255,255,255,0.20)" : "var(--color-primary)" }}
        aria-label={playing ? "Pause voice message" : "Play voice message"}
      >
        {playing ? (
          <Pause className="w-4 h-4" style={{ color: "white" }} fill="white" />
        ) : (
          <Play className="w-4 h-4 ml-0.5" style={{ color: "white" }} fill="white" />
        )}
      </button>
      <div className="flex flex-1 items-center gap-[2px] h-8">
        {voice.waveform.map((v, i) => {
          const played = i < progressIndex;
          return (
            <div
              key={i}
              className="w-[3px] rounded-full transition-opacity"
              style={{
                height: `${v * 100}%`,
                background: isOutgoing ? "white" : "var(--color-primary)",
                opacity: played ? 1 : 0.6 + v * 0.4,
                ...(isOutgoing && !played ? { opacity: 0.4 + v * 0.3 } : {}),
              }}
            />
          );
        })}
      </div>
      <span className="text-[12px] shrink-0 opacity-60 tabular-nums">{timeLabel}</span>
    </div>
  );
}

function ChatMessage({
  message,
  isOutgoing,
  position,
  showSender = false,
  showAvatar = false,
  className,
}: ChatMessageProps) {
  const timestamp = new Date(message.timestamp);
  const { currentUser } = useChatContext();
  const radiusClass = getBubbleRadius(isOutgoing, position);
  const [lightboxImage, setLightboxImage] = React.useState<string | null>(null);

  return (
    <div
      className={cn(
        "chat-message group/message relative flex items-end gap-2",
        isOutgoing ? "flex-row-reverse" : "flex-row",
        position === "first" || position === "solo" ? "mt-4" : "mt-0.5",
        className
      )}
    >
      {/* Avatar slot — 32px, only for incoming, only on last/solo */}
      {!isOutgoing ? (
        <div className="w-8 shrink-0">
          {showAvatar && message.senderAvatar ? (
            <img
              src={message.senderAvatar}
              alt={message.senderName}
              className="size-8 rounded-full object-cover"
            />
          ) : showAvatar ? (
            <div className="flex size-8 items-center justify-center rounded-full bg-muted text-[11px] font-semibold text-muted-foreground">
              {message.senderName.charAt(0).toUpperCase()}
            </div>
          ) : null}
        </div>
      ) : null}

      {/* Bubble + reactions column */}
      <div className="flex max-w-[75%] flex-col">
        {/* Sender name — only first in group, incoming */}
        {showSender && !isOutgoing && (
          <span className="mb-0.5 ml-3 text-[14px] font-semibold leading-tight tracking-[-0.01em] text-muted-foreground">
            {message.senderName}
          </span>
        )}

        {/* Bubble — relative for hover toolbar positioning */}
        <div className="relative">
          {/* Hover actions toolbar — disabled for now */}
          {/* <ChatMessageActions message={message} isOutgoing={isOutgoing} /> */}

          <div
            className={cn(
              "chat-bubble relative px-3.5 py-2",
              isOutgoing ? "bg-primary text-primary-foreground" : "bg-muted text-foreground",
              radiusClass
            )}
          >
            {/* Quoted reply */}
            {message.replyTo && (
              <ChatMessageReply replyTo={message.replyTo} isOutgoing={isOutgoing} />
            )}

            {/* Text content */}
            {message.text && (
              <p className="whitespace-pre-wrap break-words text-[15px] leading-[1.35] tracking-[-0.01em]">
                {message.text}
              </p>
            )}

            {/* Images */}
            {message.images && message.images.length > 0 && (
              <div
                className={cn(
                  "mt-1.5 flex flex-wrap gap-1.5",
                  message.images.length === 1 ? "" : ""
                )}
              >
                {message.images.map((img, idx) => (
                  <button
                    key={idx}
                    onClick={() => setLightboxImage(img.url)}
                    className="cursor-pointer rounded-lg overflow-hidden"
                    aria-label="View image"
                  >
                    <img
                      src={img.url}
                      alt={img.alt || "Image"}
                      width={img.width}
                      height={img.height}
                      className="max-h-[200px] max-w-full rounded-lg object-cover transition-opacity hover:opacity-90"
                      loading="lazy"
                    />
                  </button>
                ))}
              </div>
            )}

            {/* Code block */}
            {message.code && (
              <div className="chat-content-card mt-1.5 overflow-hidden">
                <div className="flex items-center justify-between bg-muted px-3 py-1.5">
                  <span className="text-[11px] font-medium text-muted-foreground/60">
                    {message.code.language}
                  </span>
                </div>
                <pre className="overflow-x-auto bg-muted px-3 py-2">
                  <code className="text-[13px] leading-relaxed text-foreground font-mono">
                    {message.code.code}
                  </code>
                </pre>
              </div>
            )}

            {/* File attachments */}
            {message.files && message.files.length > 0 && (
              <div className="mt-1.5 flex flex-col gap-1.5">
                {message.files.map((file, idx) => (
                  <div key={idx} className="chat-content-card flex items-center gap-2.5 px-3 py-2">
                    <div className="flex size-8 shrink-0 items-center justify-center rounded-md bg-accent">
                      <Paperclip className="size-3.5 text-primary" />
                    </div>
                    <div className="min-w-0 flex-1">
                      <p className="truncate text-[13px] font-medium text-foreground">
                        {file.name}
                      </p>
                      <p className="text-[11px] text-muted-foreground/60">
                        {file.size < 1024
                          ? `${file.size} B`
                          : file.size < 1048576
                            ? `${(file.size / 1024).toFixed(0)} KB`
                            : `${(file.size / 1048576).toFixed(1)} MB`}
                      </p>
                    </div>
                  </div>
                ))}
              </div>
            )}

            {/* Link preview */}
            {message.linkPreview && (
              <a
                href={message.linkPreview.url}
                target="_blank"
                rel="noopener noreferrer"
                className="chat-content-card mt-1.5 block hover:opacity-90 transition-opacity"
              >
                {message.linkPreview.image && (
                  <img
                    src={message.linkPreview.image}
                    alt=""
                    className="h-32 w-full object-cover"
                    loading="lazy"
                  />
                )}
                <div className="px-3 py-2">
                  <p className="text-[13px] font-semibold text-foreground">
                    {message.linkPreview.title}
                  </p>
                  <p className="mt-0.5 text-[12px] text-muted-foreground">
                    {message.linkPreview.description}
                  </p>
                  <p className="mt-1 text-[11px] text-primary">{message.linkPreview.url}</p>
                </div>
              </a>
            )}

            {/* Voice message */}
            {message.voice && <ChatVoiceMessage voice={message.voice} isOutgoing={isOutgoing} />}

            {/* Inline timestamp + status + edited label */}
            <div
              className={cn(
                "mt-1 flex items-center gap-1",
                isOutgoing ? "justify-end" : "justify-start"
              )}
            >
              {message.isEdited && <span className="text-[10px] italic opacity-50">edited</span>}
              <time className="text-[11px] tracking-[0.02em] opacity-60">
                {formatTimestamp(timestamp)}
              </time>
              {isOutgoing && message.status && <ChatMessageStatus status={message.status} />}
            </div>
          </div>

          {/* Pin indicator */}
          {message.isPinned && (
            <div className="absolute -top-1.5 -right-1.5">
              <Pin className="size-3 rotate-45 text-orange-500" />
            </div>
          )}
        </div>

        {/* Reactions bar */}
        {message.reactions && message.reactions.length > 0 && (
          <ChatMessageReactions
            messageId={message.id}
            reactions={message.reactions}
            isOutgoing={isOutgoing}
            currentUserId={currentUser.id}
          />
        )}

        {/* Read receipts (group chat) — small stacked avatars */}
        {message.readBy && message.readBy.length > 0 && (
          <ChatReadReceipts readBy={message.readBy} isOutgoing={isOutgoing} />
        )}
      </div>

      {/* Image lightbox */}
      {lightboxImage &&
        typeof document !== "undefined" &&
        createPortal(
          <div
            className="fixed inset-0 z-[9999] flex items-center justify-center bg-black/80 backdrop-blur-sm"
            onClick={() => setLightboxImage(null)}
          >
            <button
              onClick={() => setLightboxImage(null)}
              className="absolute top-4 right-4 flex size-10 items-center justify-center rounded-full bg-white/10 text-white transition-colors hover:bg-white/20"
              aria-label="Close lightbox"
            >
              <X className="size-5" />
            </button>
            <img
              src={lightboxImage}
              alt=""
              className="max-h-[90vh] max-w-[90vw] rounded-lg object-contain"
              onClick={(e) => e.stopPropagation()}
            />
          </div>,
          document.body
        )}
    </div>
  );
}

// ─── Bubble radius helper ─────────────────────────────────────────────────────

function getBubbleRadius(
  isOutgoing: boolean,
  position: "solo" | "first" | "middle" | "last"
): string {
  if (isOutgoing) {
    switch (position) {
      case "solo":
        return "rounded-[18px_18px_4px_18px]";
      case "first":
        return "rounded-[18px_18px_4px_18px]";
      case "middle":
        return "rounded-[18px_4px_4px_18px]";
      case "last":
        return "rounded-[18px_4px_18px_18px]";
    }
  } else {
    switch (position) {
      case "solo":
        return "rounded-[18px_18px_18px_4px]";
      case "first":
        return "rounded-[18px_18px_18px_4px]";
      case "middle":
        return "rounded-[4px_18px_18px_4px]";
      case "last":
        return "rounded-[4px_18px_18px_18px]";
    }
  }
}

// ─── ChatMessageStatus ────────────────────────────────────────────────────────

function ChatMessageStatus({ status }: { status: NonNullable<ChatMessageData["status"]> }) {
  switch (status) {
    case "sending":
      return <Clock className="size-3 animate-pulse opacity-50" />;
    case "sent":
      return <Check className="size-3 opacity-60" />;
    case "delivered":
      return <CheckCheck className="size-3.5 opacity-60" />;
    case "read":
      return <CheckCheck className="chat-status-read size-3.5 text-primary" />;
    case "failed":
      return <AlertCircle className="size-3.5 cursor-pointer text-destructive" />;
  }
}

// ─── ChatMessageReactions (interactive) ───────────────────────────────────────

function ChatMessageReactions({
  messageId,
  reactions,
  isOutgoing,
  currentUserId,
}: {
  messageId: string;
  reactions: NonNullable<ChatMessageData["reactions"]>;
  isOutgoing: boolean;
  currentUserId: string;
}) {
  const { onReactionAdd, onReactionRemove } = useChatContext();

  return (
    <div className={cn("mt-1 flex flex-wrap gap-1", isOutgoing ? "justify-end" : "justify-start")}>
      {reactions.map((r) => {
        const hasReacted = r.userIds.includes(currentUserId);
        return (
          <button
            key={r.emoji}
            onClick={() => {
              if (hasReacted) {
                onReactionRemove?.(messageId, r.emoji);
              } else {
                onReactionAdd?.(messageId, r.emoji);
              }
            }}
            className={cn(
              "chat-reaction-pop flex h-[26px] items-center gap-1 rounded-full border px-2 text-xs tabular-nums transition-all hover:scale-105",
              hasReacted ? "border-primary/30 bg-accent" : "border-border bg-card hover:bg-accent"
            )}
            aria-label={`${r.emoji} ${r.count} reaction${r.count !== 1 ? "s" : ""}`}
          >
            <span className="text-sm">{r.emoji}</span>
            <span
              className={cn(
                "text-[12px] font-medium",
                hasReacted ? "text-primary" : "text-muted-foreground"
              )}
            >
              {r.count}
            </span>
          </button>
        );
      })}
      {/* Add reaction button — visible on hover */}
      <button
        onClick={() => {
          // Toggle first available reaction for demo; in prod this opens a picker
          onReactionAdd?.(messageId, "\u{1F44D}");
        }}
        className="flex size-[26px] items-center justify-center rounded-full border border-dashed border-border text-muted-foreground/60 opacity-0 transition-all hover:border-primary hover:text-primary group-hover/message:opacity-100"
        aria-label="Add reaction"
      >
        <SmilePlus className="size-3" />
      </button>
    </div>
  );
}

// ─── ChatReadReceipts (group chat — stacked mini avatars) ─────────────────────

function ChatReadReceipts({
  readBy,
  isOutgoing,
}: {
  readBy: NonNullable<ChatMessageData["readBy"]>;
  isOutgoing: boolean;
}) {
  const maxVisible = 3;
  const visible = readBy.slice(0, maxVisible);
  const overflow = readBy.length - maxVisible;

  return (
    <div className={cn("mt-1 flex items-center", isOutgoing ? "justify-end" : "justify-start")}>
      <div className="flex -space-x-1.5">
        {visible.map((user) => (
          <div
            key={user.userId}
            className="flex size-4 items-center justify-center rounded-full border border-background bg-muted text-[7px] font-bold text-muted-foreground"
            title={user.name}
          >
            {user.avatar ? (
              <img
                src={user.avatar}
                alt={user.name}
                className="size-full rounded-full object-cover"
              />
            ) : (
              user.name.charAt(0).toUpperCase()
            )}
          </div>
        ))}
      </div>
      {overflow > 0 && (
        <span className="ml-1 text-[10px] text-muted-foreground/60">+{overflow}</span>
      )}
    </div>
  );
}

// ─── ChatMessageGroup ─────────────────────────────────────────────────────────

interface ChatMessageGroupProps {
  group: MessageGroup;
  className?: string;
}

function ChatMessageGroup({ group, className }: ChatMessageGroupProps) {
  const len = group.messages.length;

  return (
    <div
      className={cn(
        "chat-message-group",
        group.isOutgoing ? "items-end" : "items-start",
        className
      )}
    >
      {group.messages.map((msg, i) => {
        const position: "solo" | "first" | "middle" | "last" =
          len === 1 ? "solo" : i === 0 ? "first" : i === len - 1 ? "last" : "middle";

        return (
          <ChatMessage
            key={msg.id}
            message={msg}
            isOutgoing={group.isOutgoing}
            position={position}
            showSender={i === 0}
            showAvatar={position === "solo" || position === "last"}
          />
        );
      })}
    </div>
  );
}

// ─── ChatDateSeparator ────────────────────────────────────────────────────────

interface ChatDateSeparatorProps {
  label: string;
  className?: string;
}

function ChatDateSeparator({ label, className }: ChatDateSeparatorProps) {
  return (
    <div className={cn("chat-date-separator my-6 flex items-center gap-4", className)}>
      <div className="h-px flex-1 bg-border" />
      <span className="text-[11px] font-semibold uppercase tracking-[0.08em] text-muted-foreground/60">
        {label}
      </span>
      <div className="h-px flex-1 bg-border" />
    </div>
  );
}

// ─── ChatSystemMessage ────────────────────────────────────────────────────────

interface ChatSystemMessageProps {
  message: ChatMessageData;
  className?: string;
}

function ChatSystemMessage({ message, className }: ChatSystemMessageProps) {
  return (
    <div className={cn("chat-system-message my-4 flex justify-center", className)}>
      <span className="text-[13px] font-medium tracking-[0.01em] text-muted-foreground">
        {message.text || message.systemEvent}
      </span>
    </div>
  );
}

// ─── ChatTypingIndicator ──────────────────────────────────────────────────────

interface ChatTypingIndicatorProps {
  users: TypingUser[];
  className?: string;
}

function ChatTypingIndicator({ users, className }: ChatTypingIndicatorProps) {
  const { labels } = useChatContext();
  if (users.length === 0) return null;

  const label =
    users.length === 1
      ? (labels?.typingOne?.replace("{{name}}", users[0]!.name) ?? `${users[0]!.name} is typing`)
      : users.length === 2
        ? (labels?.typingTwo
            ?.replace("{{name1}}", users[0]!.name)
            .replace("{{name2}}", users[1]!.name) ??
          `${users[0]!.name} and ${users[1]!.name} are typing`)
        : (labels?.typingMany ?? "Several people are typing");

  return (
    <div className={cn("chat-message mt-4 flex items-end gap-2", className)}>
      {/* Avatar */}
      <div className="flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-[11px] font-semibold text-muted-foreground">
        {users[0]!.avatar ? (
          <img
            src={users[0]!.avatar}
            alt={users[0]!.name}
            className="size-full rounded-full object-cover"
          />
        ) : (
          users[0]!.name.charAt(0).toUpperCase()
        )}
      </div>

      <div className="flex flex-col">
        {/* Label */}
        <span className="mb-0.5 ml-3 text-[12px] text-muted-foreground/60">{label}</span>

        {/* Dots bubble */}
        <div className="flex w-16 items-center justify-center gap-1 rounded-[18px_18px_18px_4px] bg-muted px-4 py-3">
          <span
            className="chat-typing-dot size-[7px] rounded-full bg-muted-foreground"
            style={{ animationDelay: "0ms" }}
          />
          <span
            className="chat-typing-dot size-[7px] rounded-full bg-muted-foreground"
            style={{ animationDelay: "160ms" }}
          />
          <span
            className="chat-typing-dot size-[7px] rounded-full bg-muted-foreground"
            style={{ animationDelay: "320ms" }}
          />
        </div>
      </div>
    </div>
  );
}

// ─── ChatReplyPreview (bar above composer) ────────────────────────────────────

interface ChatReplyPreviewProps {
  replyingTo: ChatMessageData;
  onCancel: () => void;
  className?: string;
}

function ChatReplyPreview({ replyingTo, onCancel, className }: ChatReplyPreviewProps) {
  return (
    <div
      className={cn("flex items-center gap-3 border-t border-border bg-card px-4 py-2", className)}
    >
      <div className="h-8 w-0.5 shrink-0 rounded-full bg-primary" />
      <div className="min-w-0 flex-1">
        <span className="block text-[12px] font-semibold text-primary">
          {replyingTo.senderName}
        </span>
        <span className="block truncate text-[13px] text-muted-foreground">{replyingTo.text}</span>
      </div>
      <button
        onClick={onCancel}
        className="flex size-6 shrink-0 items-center justify-center rounded-full text-muted-foreground/60 transition-colors hover:bg-accent hover:text-foreground"
        aria-label="Cancel reply"
      >
        <X className="size-3.5" />
      </button>
    </div>
  );
}

// ─── ChatMessages (scroll container) ──────────────────────────────────────────

interface ChatMessagesProps {
  messages: ChatMessageData[];
  typingUsers?: TypingUser[];
  className?: string;
  onLoadMore?: () => Promise<void>;
  hasMore?: boolean;
}

function ChatMessages({ messages, typingUsers = [], className }: ChatMessagesProps) {
  const { currentUser, messageGroupingInterval, labels } = useChatContext();
  const { containerRef, scrollToBottom, isAtBottom, unseenCount } = useAutoScroll(messages);

  const items = React.useMemo(
    () => groupMessages(messages, currentUser.id, messageGroupingInterval),
    [messages, currentUser.id, messageGroupingInterval]
  );

  return (
    <div className={cn("chat-messages relative flex flex-1 flex-col overflow-hidden", className)}>
      {/* Scrollable area */}
      <div
        ref={containerRef}
        className="flex-1 overflow-y-auto px-4 py-4"
        role="log"
        aria-live="polite"
      >
        <div className="w-full">
          {items.map((item, i) => {
            switch (item.type) {
              case "date":
                return <ChatDateSeparator key={`date-${item.label}-${i}`} label={item.label} />;
              case "system":
                return <ChatSystemMessage key={item.message.id} message={item.message} />;
              case "group":
                return (
                  <ChatMessageGroup
                    key={`group-${item.group.messages[0]!.id}`}
                    group={item.group}
                  />
                );
            }
          })}

          {/* Typing indicator at the bottom */}
          {typingUsers.length > 0 && <ChatTypingIndicator users={typingUsers} />}
        </div>
      </div>

      {/* Scroll-to-bottom FAB with unread badge */}
      <button
        onClick={() => scrollToBottom("smooth")}
        className={cn(
          "absolute bottom-4 right-4 z-5 flex size-10 items-center justify-center rounded-full border border-border bg-background shadow-md transition-all duration-200",
          isAtBottom ? "pointer-events-none translate-y-2 opacity-0" : "translate-y-0 opacity-100"
        )}
        aria-label={labels?.scrollToBottom ?? "Scroll to bottom"}
      >
        <ChevronDown className="size-[18px] text-muted-foreground" />
        {/* Unread badge */}
        {unseenCount > 0 && (
          <span className="absolute -top-1 -right-1 flex size-[18px] items-center justify-center rounded-full bg-primary text-[11px] font-bold text-white tabular-nums">
            {unseenCount > 99 ? "99+" : unseenCount}
          </span>
        )}
      </button>
    </div>
  );
}

// ─── File preview item ────────────────────────────────────────────────────────

interface FilePreviewItem {
  file: File;
  id: string;
  preview?: string; // data URL for images
  progress?: number; // 0-100
}

function ChatFilePreview({ item, onRemove }: { item: FilePreviewItem; onRemove: () => void }) {
  const isImage = item.file.type.startsWith("image/");

  return (
    <div className="relative shrink-0 rounded-lg border border-border bg-card">
      {isImage && item.preview ? (
        <div className="relative size-14 overflow-hidden rounded-lg">
          <img src={item.preview} alt={item.file.name} className="size-full object-cover" />
        </div>
      ) : (
        <div className="flex items-center gap-2 px-3 py-2">
          <div className="flex size-8 items-center justify-center rounded-md bg-accent">
            <Paperclip className="size-3.5 text-primary" />
          </div>
          <div className="min-w-0">
            <p className="max-w-[120px] truncate text-[12px] font-medium text-foreground">
              {item.file.name}
            </p>
            <p className="text-[10px] text-muted-foreground/60">
              {(item.file.size / 1024).toFixed(0)} KB
            </p>
          </div>
        </div>
      )}
      {/* Progress bar */}
      {item.progress !== undefined && item.progress < 100 && (
        <div className="absolute bottom-0 left-0 h-[3px] w-full bg-border">
          <div
            className="h-full bg-primary transition-all"
            style={{ width: `${item.progress}%` }}
          />
        </div>
      )}
      {/* Remove button */}
      <button
        onClick={onRemove}
        className="absolute -top-1.5 -right-1.5 flex size-5 items-center justify-center rounded-full border border-border bg-card text-muted-foreground shadow-sm hover:bg-accent hover:text-foreground"
        aria-label="Remove file"
      >
        <X className="size-3" />
      </button>
    </div>
  );
}

// ─── ChatComposer ─────────────────────────────────────────────────────────────

interface ChatComposerProps {
  onSend?: (text: string) => void;
  onTyping?: (isTyping: boolean) => void;
  onFileUpload?: (files: File[]) => void;
  onVoiceRecord?: () => void;
  placeholder?: string;
  disabled?: boolean;
  replyingTo?: ChatMessageData | null;
  onCancelReply?: () => void;
  className?: string;
}

function ChatComposer({
  onSend,
  onTyping,
  onFileUpload,
  onVoiceRecord,
  placeholder = "Message",
  disabled = false,
  replyingTo,
  onCancelReply,
  className,
}: ChatComposerProps) {
  const [value, setValue] = React.useState("");
  const [files, setFiles] = React.useState<FilePreviewItem[]>([]);
  const [isDragging, setIsDragging] = React.useState(false);
  // const [showAttachMenu, setShowAttachMenu] = React.useState(false)
  const { textareaRef, resize } = useAutoResize({ maxRows: 6 });
  const { handleKeyDown: handleTypingKeyDown, stopTyping } = useTypingIndicator({
    onTypingChange: onTyping,
  });
  // const fileInputRef = React.useRef<HTMLInputElement>(null)
  // const imageInputRef = React.useRef<HTMLInputElement>(null)
  const hasContent = value.trim().length > 0 || files.length > 0;

  const addFiles = React.useCallback(
    (newFiles: FileList | File[]) => {
      const arr = Array.from(newFiles);
      const items: FilePreviewItem[] = arr.map((f) => ({
        file: f,
        id: `${f.name}-${Date.now()}-${Math.random()}`,
        progress: undefined,
      }));

      // Generate image previews
      items.forEach((item) => {
        if (item.file.type.startsWith("image/")) {
          const reader = new FileReader();
          reader.onload = (e) => {
            setFiles((prev) =>
              prev.map((f) =>
                f.id === item.id ? { ...f, preview: e.target?.result as string } : f
              )
            );
          };
          reader.readAsDataURL(item.file);
        }
      });

      setFiles((prev) => [...prev, ...items]);
      onFileUpload?.(arr);
    },
    [onFileUpload]
  );

  const removeFile = React.useCallback((id: string) => {
    setFiles((prev) => prev.filter((f) => f.id !== id));
  }, []);

  const handleSend = React.useCallback(() => {
    const trimmed = value.trim();
    if ((!trimmed && files.length === 0) || disabled) return;
    if (trimmed) onSend?.(trimmed);
    setValue("");
    setFiles([]);
    stopTyping();
    if (textareaRef.current) textareaRef.current.style.height = "auto";
  }, [value, files, disabled, onSend, textareaRef, stopTyping]);

  const handleKeyDown = React.useCallback(
    (e: React.KeyboardEvent) => {
      handleTypingKeyDown();
      if (e.key === "Enter" && !e.shiftKey) {
        e.preventDefault();
        handleSend();
      }
      if (e.key === "Escape" && replyingTo) onCancelReply?.();
    },
    [handleSend, handleTypingKeyDown, replyingTo, onCancelReply]
  );

  // Paste upload
  const handlePaste = React.useCallback(
    (e: React.ClipboardEvent) => {
      const items = e.clipboardData?.items;
      if (!items) return;
      const imageFiles: File[] = [];
      for (const item of Array.from(items)) {
        if (item.type.startsWith("image/")) {
          const file = item.getAsFile();
          if (file) imageFiles.push(file);
        }
      }
      if (imageFiles.length > 0) {
        addFiles(imageFiles);
      }
    },
    [addFiles]
  );

  // Drag-and-drop handlers (on the composer container)
  const handleDragOver = React.useCallback((e: React.DragEvent) => {
    e.preventDefault();
    setIsDragging(true);
  }, []);
  const handleDragLeave = React.useCallback((e: React.DragEvent) => {
    e.preventDefault();
    setIsDragging(false);
  }, []);
  const handleDrop = React.useCallback(
    (e: React.DragEvent) => {
      e.preventDefault();
      setIsDragging(false);
      if (e.dataTransfer.files.length > 0) {
        addFiles(e.dataTransfer.files);
      }
    },
    [addFiles]
  );

  return (
    <div
      className={cn("chat-composer sticky bottom-0 z-10 relative", className)}
      onDragOver={handleDragOver}
      onDragLeave={handleDragLeave}
      onDrop={handleDrop}
    >
      {/* Drop overlay */}
      {isDragging && (
        <div className="chat-drop-overlay">
          <div className="flex flex-col items-center gap-2">
            <Upload className="size-8 text-primary" />
            <span className="text-[14px] font-medium text-primary">Drop files to upload</span>
          </div>
        </div>
      )}

      {/* Reply preview bar */}
      {replyingTo && (
        <ChatReplyPreview replyingTo={replyingTo} onCancel={() => onCancelReply?.()} />
      )}

      {/* File preview strip */}
      {files.length > 0 && (
        <div className="flex gap-3 overflow-x-auto border-t border-border bg-card px-4 pt-3 pb-2 backdrop-blur-[20px]">
          {files.map((f) => (
            <ChatFilePreview key={f.id} item={f} onRemove={() => removeFile(f.id)} />
          ))}
        </div>
      )}

      {/* Composer body — frosted glass */}
      <div className="bg-card px-3 py-2">
        <div>
          {/* Input row */}
          <div className="flex items-end gap-2">
            {/* + button with attachment popout — disabled until file upload is implemented
            <div className="relative">
              <button
                onClick={() => setShowAttachMenu(!showAttachMenu)}
                className={cn(
                  "flex size-9 items-center justify-center rounded-full border border-border bg-card text-muted-foreground/60 transition-all hover:bg-accent hover:text-muted-foreground",
                  showAttachMenu && "rotate-45 bg-accent text-primary"
                )}
                aria-label="Attachments"
              >
                <Plus className="size-5" />
              </button>

              {showAttachMenu && (
                <div className="chat-toolbar-enter absolute bottom-full left-0 mb-2 w-44 overflow-hidden rounded-xl border border-border bg-card py-1 shadow-md">
                  <button
                    onClick={() => { fileInputRef.current?.click(); setShowAttachMenu(false) }}
                    className="flex w-full items-center gap-2.5 px-3 py-2 text-[13px] text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
                  >
                    <Paperclip className="size-4" />
                    Attach file
                  </button>
                  <button
                    onClick={() => { imageInputRef.current?.click(); setShowAttachMenu(false) }}
                    className="flex w-full items-center gap-2.5 px-3 py-2 text-[13px] text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
                  >
                    <ImageIcon className="size-4" />
                    Photo or video
                  </button>
                  <button
                    onClick={() => setShowAttachMenu(false)}
                    className="flex w-full items-center gap-2.5 px-3 py-2 text-[13px] text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
                  >
                    <Smile className="size-4" />
                    Emoji
                  </button>
                </div>
              )}
            </div>

            <input ref={fileInputRef} type="file" multiple className="hidden" onChange={(e) => { if (e.target.files) addFiles(e.target.files); e.target.value = "" }} />
            <input ref={imageInputRef} type="file" accept="image/*" multiple className="hidden" onChange={(e) => { if (e.target.files) addFiles(e.target.files); e.target.value = "" }} />
            */}

            <div className="relative flex flex-1 items-end rounded-[22px] border border-border bg-card">
              <textarea
                ref={textareaRef}
                value={value}
                onChange={(e) => {
                  setValue(e.target.value);
                  resize();
                }}
                onKeyDown={handleKeyDown}
                onPaste={handlePaste}
                placeholder={placeholder}
                disabled={disabled}
                rows={1}
                className="flex-1 resize-none bg-transparent py-[10px] pl-4 pr-12 text-[15px] leading-[22px] tracking-[-0.01em] text-foreground placeholder:text-muted-foreground/60 focus:outline-none disabled:opacity-50"
                style={{ overflow: "hidden", maxHeight: "160px" }}
              />

              {!hasContent && onVoiceRecord ? (
                <button
                  onClick={onVoiceRecord}
                  disabled={disabled}
                  className="absolute bottom-[6px] right-[6px] flex size-8 items-center justify-center rounded-full text-muted-foreground/60 transition-colors hover:text-primary"
                  aria-label="Record voice message"
                >
                  <Mic className="size-4" strokeWidth={2.5} />
                </button>
              ) : (
                <button
                  onClick={handleSend}
                  disabled={!hasContent || disabled}
                  className={cn(
                    "absolute bottom-[6px] right-[6px] flex size-8 items-center justify-center rounded-full transition-all duration-200",
                    hasContent
                      ? "bg-primary text-white hover:scale-105 active:scale-95"
                      : "bg-transparent text-muted-foreground/60"
                  )}
                  aria-label="Send message"
                >
                  <ArrowUp className="size-4" strokeWidth={2.5} />
                </button>
              )}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

// ─── Exports ──────────────────────────────────────────────────────────────────

export {
  ChatProvider,
  ChatMessage,
  ChatMessageGroup,
  ChatDateSeparator,
  ChatSystemMessage,
  ChatMessages,
  ChatComposer,
  ChatMessageStatus,
  ChatMessageReactions,
  ChatMessageActions,
  ChatMessageReply,
  ChatTypingIndicator,
  ChatReplyPreview,
  ChatReadReceipts,
};
export type {
  ChatProviderProps,
  ChatMessageProps,
  ChatMessageGroupProps,
  ChatDateSeparatorProps,
  ChatSystemMessageProps,
  ChatMessagesProps,
  ChatComposerProps,
  ChatMessageActionsProps,
  ChatTypingIndicatorProps,
  ChatReplyPreviewProps,
};
