import { useCallback, useEffect, useRef, useState } from "react";
import type { Channel } from "@/types/Channel";
import type { Message } from "@/types/Message";
import { getChannels, getMessages, getCurrentUsername, deleteMessage } from "@/api";
import { ChannelSidebar } from "@/components/ChannelSidebar";
import { MessageItem } from "@/components/MessageItem";
import { ComposeBox } from "@/components/ComposeBox";
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Skeleton } from "@/components/ui/skeleton";
import { useChannelSocket } from "@/hooks/useChannelSocket";

function GatePage() {
  return (
    <div className="h-full flex flex-col items-center justify-center bg-background text-foreground px-6">
      <span className="text-8xl mb-6">🐒</span>
      <h1 className="font-sans text-3xl md:text-5xl font-bold uppercase tracking-wider mb-4">
        Ape Colony
      </h1>
      <p className="font-mono text-sm text-muted-foreground mb-8 text-center max-w-md">
        this is not a place for humans without names.
        <br />
        identify yourself, ape.
      </p>
      <div className="font-mono text-xs text-muted-foreground/50 border-2 border-border px-4 py-3 max-w-sm">
        <div className="text-primary font-bold mb-1">// add ?user= to the URL</div>
        <div>apes.unslope.com<span className="text-primary">?user=benji</span></div>
        <div>apes.unslope.com<span className="text-primary">?user=neeraj</span></div>
      </div>
    </div>
  );
}

export default function App() {
  // Gate: require ?user= param in URL (not just localStorage)
  const urlUser = new URL(window.location.href).searchParams.get("user");
  const storedUser = localStorage.getItem("colony_user");

  if (!urlUser && !storedUser) {
    return <GatePage />;
  }
  const [channels, setChannels] = useState<Channel[]>([]);
  const [activeChannelId, setActiveChannelId] = useState<string | null>(null);
  const [messages, setMessages] = useState<Message[]>([]);
  const [loading, setLoading] = useState(false);
  const [selectedMessages, setSelectedMessages] = useState<{ id: string; username: string; content: string }[]>([]);
  const [sheetOpen, setSheetOpen] = useState(false);
  const [showScrollDown, setShowScrollDown] = useState(false);
  const scrollRef = useRef<HTMLDivElement>(null);
  const prevMsgCountRef = useRef(0);
  const activeChannelRef = useRef(activeChannelId);

  activeChannelRef.current = activeChannelId;

  const loadChannels = useCallback(async () => {
    const chs = await getChannels();
    setChannels(chs);
    setActiveChannelId((prev) => (prev ? prev : chs[0]?.id ?? null));
  }, []);

  const loadMessages = useCallback(async () => {
    const channelId = activeChannelRef.current;
    if (!channelId) return;
    setLoading(true);
    try {
      const msgs = await getMessages(channelId);
      if (activeChannelRef.current === channelId) {
        setMessages(msgs);
      }
    } catch {
      // Silently ignore fetch errors
    } finally {
      setLoading(false);
    }
  }, []);

  // WebSocket: append new messages in real-time
  const handleWsMessage = useCallback((msg: Message) => {
    setMessages((prev) => {
      if (prev.some((m) => m.id === msg.id)) return prev;
      return [...prev, msg];
    });
  }, []);

  // On WS reconnect, refetch full history to catch missed messages
  const handleWsReconnect = useCallback(() => {
    loadMessages();
  }, [loadMessages]);

  useChannelSocket(activeChannelId, handleWsMessage, handleWsReconnect);

  useEffect(() => { loadChannels(); }, [loadChannels]);

  useEffect(() => {
    setMessages([]);
    setSelectedMessages([]);
    prevMsgCountRef.current = 0;
    loadMessages();
  }, [activeChannelId, loadMessages]);

  function getViewport() {
    const el = scrollRef.current as unknown as HTMLElement | null;
    return el?.querySelector('[data-slot="scroll-area-viewport"]') as HTMLElement | null ?? el;
  }

  function scrollToBottom(smooth = false) {
    const vp = getViewport();
    if (vp) vp.scrollTo({ top: vp.scrollHeight, behavior: smooth ? "smooth" : "instant" });
  }

  // Auto-scroll only on new messages
  useEffect(() => {
    if (messages.length > prevMsgCountRef.current) {
      scrollToBottom();
    }
    prevMsgCountRef.current = messages.length;
  }, [messages]);

  // Track scroll position for scroll-down button
  useEffect(() => {
    const vp = getViewport();
    if (!vp) return;
    function onScroll() {
      const v = vp!;
      setShowScrollDown(v.scrollHeight - v.scrollTop - v.clientHeight > 150);
    }
    vp.addEventListener("scroll", onScroll, { passive: true });
    return () => vp.removeEventListener("scroll", onScroll);
  });

  const messagesById = new Map(messages.map((m) => [m.id, m]));
  const activeChannel = channels.find((c) => c.id === activeChannelId);

  // Sort channels by last opened (stored in localStorage)
  function getLastOpened(channelId: string): number {
    return Number(localStorage.getItem(`colony_last_opened_${channelId}`)) || 0;
  }
  function markOpened(channelId: string) {
    localStorage.setItem(`colony_last_opened_${channelId}`, String(Date.now()));
  }
  const sortedChannels = [...channels].sort((a, b) => getLastOpened(b.id) - getLastOpened(a.id));

  const sidebar = (
    <ChannelSidebar
      channels={sortedChannels}
      activeId={activeChannelId}
      onSelect={(id) => {
        markOpened(id);
        setActiveChannelId(id);
        setSheetOpen(false);
      }}
      onChannelCreated={loadChannels}
    />
  );

  return (
    <div className="flex h-full overflow-hidden">
      <div className="hidden md:flex md:flex-col md:h-full">
        {sidebar}
      </div>

      <div className="flex-1 flex flex-col min-w-0 h-full overflow-hidden">
        <div className="px-4 py-3 md:px-6 md:py-4 border-b-2 border-border flex items-center gap-3">
          <Sheet open={sheetOpen} onOpenChange={setSheetOpen}>
            <SheetTrigger className="md:hidden p-1 h-8 w-8 text-muted-foreground hover:text-primary font-mono font-bold text-lg">
              =
            </SheetTrigger>
            <SheetContent side="left" className="p-0 w-56 bg-sidebar border-r-2 border-border">
              {sidebar}
            </SheetContent>
          </Sheet>

          {activeChannel ? (
            <>
              <span className="text-primary font-bold font-mono">#</span>
              <span className="font-sans font-bold text-sm uppercase tracking-wide">{activeChannel.name}</span>
              {activeChannel.description && (
                <span className="text-xs font-mono text-muted-foreground ml-2 hidden md:inline">
                  {activeChannel.description}
                </span>
              )}
              <span className="ml-auto text-[10px] font-mono text-muted-foreground tabular-nums">
                {messages.length} msg
              </span>
            </>
          ) : (
            <span className="text-muted-foreground text-xs font-mono">no channel selected</span>
          )}
        </div>

        <ScrollArea ref={scrollRef} className="flex-1 min-h-0">
          {loading && messages.length === 0 ? (
            <div className="p-5 space-y-4">
              {[1, 2, 3].map((i) => (
                <div key={i} className="flex items-start gap-3">
                  <Skeleton className="h-6 w-6 rounded-none" />
                  <div className="flex-1 space-y-2">
                    <Skeleton className="h-3 w-24 rounded-none" />
                    <Skeleton className="h-3 w-full rounded-none" />
                  </div>
                </div>
              ))}
            </div>
          ) : messages.length === 0 && activeChannelId ? (
            <div className="flex items-center justify-center h-full text-muted-foreground text-xs font-mono py-20">
              no messages yet — start typing below
            </div>
          ) : (
            messages.map((msg, i) => {
              const prev = i > 0 ? messages[i - 1] : null;
              const sameSender = prev && prev.user.username === msg.user.username;
              const withinWindow = prev && (new Date(msg.created_at).getTime() - new Date(prev.created_at).getTime()) < 5 * 60 * 1000;
              const prevDate = prev ? new Date(prev.created_at).toDateString() : null;
              const thisDate = new Date(msg.created_at).toDateString();
              const showDate = prevDate !== thisDate;
              // Don't compact: after date break, typed messages (non-text), or replies
              const isTyped = msg.type !== "text";
              const compact = !!(sameSender && withinWindow && !msg.reply_to && !showDate && !isTyped);

              return (
                <div key={msg.id}>
                  {showDate && (
                    <div className="flex items-center gap-3 px-5 py-3">
                      <div className="flex-1 h-px bg-border" />
                      <span className="text-[10px] font-mono text-muted-foreground uppercase tracking-widest">
                        {new Date(msg.created_at).toLocaleDateString("en-US", { weekday: "short", month: "short", day: "numeric" })}
                      </span>
                      <div className="flex-1 h-px bg-border" />
                    </div>
                  )}
                  <MessageItem
                    message={msg}
                    compact={compact}
                    replyTarget={msg.reply_to ? messagesById.get(msg.reply_to) : undefined}
                    currentUsername={getCurrentUsername()}
                    selected={selectedMessages.some((s) => s.id === msg.id)}
                    onSelect={(id) => {
                      setSelectedMessages((prevSel) => {
                        const exists = prevSel.find((s) => s.id === id);
                        if (exists) return prevSel.filter((s) => s.id !== id);
                        const target = messagesById.get(id);
                        if (!target) return prevSel;
                        return [...prevSel, { id, username: target.user.display_name, content: target.content }];
                      });
                    }}
                    onDelete={async (chId, msgId) => {
                      try {
                        await deleteMessage(chId, msgId);
                        loadMessages();
                      } catch {
                        // ignore
                      }
                    }}
                  />
                </div>
              );
            })
          )}
        </ScrollArea>

        {/* Scroll-to-bottom button */}
        {showScrollDown && (
          <div className="flex justify-center -mt-6 mb-1 relative z-10">
            <button
              type="button"
              onClick={() => scrollToBottom(true)}
              className="w-8 h-8 flex items-center justify-center border-2 border-border bg-card text-muted-foreground hover:text-primary hover:border-primary transition-colors shadow-lg text-sm"
            >
              ↓
            </button>
          </div>
        )}

        {activeChannelId && (
          <ComposeBox
            key={activeChannelId}
            channelId={activeChannelId}
            replyTo={selectedMessages.length > 0 ? selectedMessages[0] : null}
            selectedMessages={selectedMessages}
            onClearReply={() => setSelectedMessages([])}
            onMessageSent={() => {
              setSelectedMessages([]);
              loadMessages();
              setTimeout(() => scrollToBottom(), 100);
            }}
          />
        )}
      </div>
    </div>
  );
}
