import { useCallback, useMemo, useState } from "react";
import { ChevronLeft } from "lucide-react";
import { useQueryClient } from "@tanstack/react-query";
import { useChatPanel } from "../context/ChatPanelContext";
import { useCompany } from "../context/CompanyContext";
import { useToast } from "../context/ToastContext";
import { useChatMessages } from "../hooks/useChatMessages";
import { useStreamingChat } from "../hooks/useStreamingChat";
import { useBrainstormerDefault } from "../hooks/useBrainstormerDefault";
import { useChatBookmarks, useToggleBookmark } from "../hooks/useChatBookmarks";
import { useChatFileUpload } from "../hooks/useChatFileUpload";
import { useOfflineQueue } from "../hooks/useOfflineQueue";
import { useOnlineStatus } from "../hooks/useOnlineStatus";
import { useChatConversations } from "../hooks/useChatConversations";
import { useQuery } from "@tanstack/react-query";
import { chatApi } from "../api/chat";
import { agentsApi } from "../api/agents";
import { resolveAgentFromContent } from "../lib/slash-commands";
import { ChatMessageList } from "./ChatMessageList";
import { ChatInput } from "./ChatInput";
import { ChatAgentSelector } from "./ChatAgentSelector";
import { ChatConversationList } from "./ChatConversationList";
import { ChatStopButton } from "./ChatStopButton";
import { PullToRefresh } from "./PullToRefresh";
import { OfflineBanner } from "./OfflineBanner";
import { InstallPromptBanner } from "./InstallPromptBanner";
import { NotificationPermissionPrompt } from "./NotificationPermissionPrompt";
import { Button } from "@/components/ui/button";
import type { AgentRole } from "@paperclipai/shared";

/**
 * Full-screen mobile chat layout.
 * - When no conversation is active: shows conversation list with pull-to-refresh
 * - When a conversation is active: shows header + message list + sticky input
 *
 * Does NOT render any MobileNavBar — relies on the global MobileBottomNav in Layout.tsx.
 */
export function MobileChatView() {
  const { activeConversationId, setActiveConversationId } = useChatPanel();
  const { selectedCompanyId } = useCompany();
  const queryClient = useQueryClient();
  const { pushToast } = useToast();
  const [isSending, setIsSending] = useState(false);
  const [activeAgentId, setActiveAgentId] = useState<string | null>(null);

  const { messages } = useChatMessages(activeConversationId);
  const { streamingContent, isStreaming, startStream, stop } = useStreamingChat(activeConversationId);
  const { pendingFiles, addFile, removeFile, clearCompleted, completedFileIds } = useChatFileUpload(activeConversationId);
  const { enqueue, queuedCount } = useOfflineQueue();
  const isOnline = useOnlineStatus();
  const brainstormerDefaultId = useBrainstormerDefault();

  // useChatConversations for refetch (pull-to-refresh)
  const { refetch } = useChatConversations(selectedCompanyId);

  // Auto-select brainstormer for new conversations
  const effectiveBrainstormerId =
    activeAgentId === null && brainstormerDefaultId !== null && !activeConversationId
      ? brainstormerDefaultId
      : activeAgentId;

  // Load agents for routing and identity
  const { data: agents = [], isLoading: agentsLoading } = useQuery({
    queryKey: ["agents", selectedCompanyId],
    queryFn: () => agentsApi.list(selectedCompanyId!),
    enabled: !!selectedCompanyId,
  });

  // Agent map for identity bars
  const agentMap = useMemo(() => {
    const map = new Map<string, { name: string; icon: string | null; role: AgentRole | null }>();
    for (const a of agents) {
      map.set(a.id, { name: a.name, icon: a.icon, role: (a.role as AgentRole) ?? null });
    }
    return map;
  }, [agents]);

  const streamingAgent = effectiveBrainstormerId
    ? agentMap.get(effectiveBrainstormerId)
    : undefined;

  // Bookmark state
  const { data: bookmarkData } = useChatBookmarks(selectedCompanyId, activeConversationId ?? undefined);
  const { toggleBookmark } = useToggleBookmark();
  const bookmarkedMessageIds = useMemo(() => {
    const ids = new Set<string>();
    for (const b of bookmarkData?.items ?? []) ids.add(b.message.id);
    return ids;
  }, [bookmarkData]);

  // Get active conversation title for header
  const { data: conversationsData } = useQuery({
    queryKey: ["chat", "conversations", selectedCompanyId, ""],
    queryFn: () => chatApi.listConversations(selectedCompanyId!, {}),
    enabled: !!selectedCompanyId && !!activeConversationId,
  });
  const activeConversation = conversationsData?.items.find((c) => c.id === activeConversationId);
  const conversationTitle = activeConversation?.title ?? "New Conversation";

  const handleHandoff = useCallback(
    async (spec: { what: string; why: string; constraints: string; success: string }) => {
      if (!activeConversationId) return;
      try {
        await chatApi.handoffSpec(activeConversationId, spec, "pm");
        queryClient.invalidateQueries({ queryKey: ["chat", "messages", activeConversationId] });
      } catch {
        pushToast({ title: "Could not send to PM. Try again.", tone: "error" });
      }
    },
    [activeConversationId, queryClient, pushToast],
  );

  const [agentResponseCount, setAgentResponseCount] = useState(0);

  const handleSend = async (content: string) => {
    if (!selectedCompanyId) return;

    // If offline, enqueue the message and show a toast
    if (!isOnline) {
      if (activeConversationId) {
        await enqueue(activeConversationId, content);
        pushToast({ title: "Message queued — will send when you're back online", tone: "info" });
      }
      return;
    }

    const resolvedAgentId = resolveAgentFromContent(content, agents, effectiveBrainstormerId);
    const fileIdsToAttach = [...completedFileIds];

    setIsSending(true);
    try {
      if (!activeConversationId) {
        const newConvo = await chatApi.createConversation(selectedCompanyId, {
          agentId: resolvedAgentId ?? undefined,
        });
        setActiveConversationId(newConvo.id);
        const message = await chatApi.postMessage(newConvo.id, { role: "user", content });
        if (fileIdsToAttach.length > 0) {
          await chatApi.attachFilesToMessage(fileIdsToAttach, message.id);
          clearCompleted();
          queryClient.invalidateQueries({ queryKey: ["chat", "messages", newConvo.id] });
        }
        queryClient.invalidateQueries({ queryKey: ["chat"] });
        startStream(content, resolvedAgentId ?? undefined);
      } else {
        const message = await chatApi.postMessage(activeConversationId, { role: "user", content });
        if (fileIdsToAttach.length > 0) {
          await chatApi.attachFilesToMessage(fileIdsToAttach, message.id);
          clearCompleted();
        }
        queryClient.invalidateQueries({ queryKey: ["chat", "messages", activeConversationId] });
        startStream(content, resolvedAgentId ?? undefined);
      }
    } finally {
      setIsSending(false);
    }
  };

  const handleEdit = async (messageId: string, newContent: string) => {
    if (!activeConversationId) return;
    const editedIdx = messages.findIndex((m) => m.id === messageId);
    const hasSubsequentMessages = editedIdx >= 0 && editedIdx < messages.length - 1;

    if (hasSubsequentMessages) {
      try {
        const newConv = await chatApi.branchConversation(activeConversationId, messageId);
        setActiveConversationId(newConv.id);
        queryClient.invalidateQueries({ queryKey: ["chat", "branches"] });
        queryClient.invalidateQueries({ queryKey: ["chat"] });
        await chatApi.editMessage(newConv.id, messageId, newContent);
        await chatApi.truncateMessagesAfter(newConv.id, messageId);
        queryClient.invalidateQueries({ queryKey: ["chat", "messages", newConv.id] });
        startStream(newContent, effectiveBrainstormerId ?? undefined);
      } catch {
        pushToast({ title: "Could not create branch. Try again.", tone: "error" });
      }
    } else {
      await chatApi.editMessage(activeConversationId, messageId, newContent);
      await chatApi.truncateMessagesAfter(activeConversationId, messageId);
      queryClient.invalidateQueries({ queryKey: ["chat", "messages", activeConversationId] });
      startStream(newContent, effectiveBrainstormerId ?? undefined);
    }
  };

  const handleRetry = async (assistantMessageId: string) => {
    if (!activeConversationId || !messages) return;
    const assistantIdx = messages.findIndex((m) => m.id === assistantMessageId);
    if (assistantIdx < 0) return;

    let lastUserContent = "";
    let userMessageId = "";
    for (let i = assistantIdx - 1; i >= 0; i--) {
      if (messages[i]!.role === "user") {
        lastUserContent = messages[i]!.content;
        userMessageId = messages[i]!.id;
        break;
      }
    }
    if (!lastUserContent || !userMessageId) return;

    await chatApi.truncateMessagesAfter(activeConversationId, userMessageId);
    queryClient.invalidateQueries({ queryKey: ["chat", "messages", activeConversationId] });
    startStream(lastUserContent, effectiveBrainstormerId ?? undefined);
  };

  const handleBookmark = useCallback(
    (messageId: string) => {
      if (!activeConversationId) return;
      toggleBookmark({ conversationId: activeConversationId, messageId });
    },
    [activeConversationId, toggleBookmark],
  );

  const handleRefresh = async () => {
    await refetch();
  };

  // Conversation list view (no active conversation)
  if (!activeConversationId) {
    return (
      <div className="fixed inset-0 z-40 flex h-[100dvh] flex-col bg-background">
        <OfflineBanner queuedCount={queuedCount} />
        <InstallPromptBanner />
        {/* pb-16 accounts for the existing MobileBottomNav (h-16) at the bottom of Layout */}
        <div className="flex-1 overflow-hidden pb-16">
          {selectedCompanyId ? (
            <PullToRefresh onRefresh={handleRefresh} enabled={true}>
              <ChatConversationList companyId={selectedCompanyId} />
            </PullToRefresh>
          ) : (
            <div className="flex h-full items-center justify-center p-4">
              <p className="text-sm text-muted-foreground">No workspace selected</p>
            </div>
          )}
        </div>
      </div>
    );
  }

  // Active conversation view
  return (
    <div className="fixed inset-0 z-40 flex h-[100dvh] flex-col bg-background">
      <OfflineBanner queuedCount={queuedCount} />
      <NotificationPermissionPrompt agentResponseCount={agentResponseCount} />
      {/* Header: 48px tall */}
      <div className="flex h-12 flex-shrink-0 items-center gap-2 border-b border-border px-3">
        <Button
          variant="ghost"
          size="icon"
          className="h-8 w-8"
          onClick={() => setActiveConversationId(null)}
          aria-label="Back to conversations"
        >
          <ChevronLeft className="h-5 w-5" />
        </Button>
        <span className="flex-1 truncate text-sm font-medium">{conversationTitle}</span>
        {selectedCompanyId && (
          <ChatAgentSelector
            companyId={selectedCompanyId}
            conversationId={activeConversationId}
            agentId={effectiveBrainstormerId}
            onAgentChange={setActiveAgentId}
          />
        )}
      </div>

      {/* Message list: fills remaining space */}
      <div className="flex-1 overflow-hidden">
        <ChatMessageList
          conversationId={activeConversationId}
          streamingContent={streamingContent}
          isStreaming={isStreaming}
          streamingAgentName={streamingAgent?.name ?? null}
          streamingAgentIcon={streamingAgent?.icon ?? null}
          streamingAgentRole={streamingAgent?.role ?? null}
          onEdit={handleEdit}
          onRetry={handleRetry}
          onHandoff={handleHandoff}
          agentMap={agentMap}
          onBookmark={handleBookmark}
          bookmarkedMessageIds={bookmarkedMessageIds}
        />
      </div>

      {/* Stop button (shown during streaming) */}
      {isStreaming && <ChatStopButton onStop={stop} />}

      {/* Sticky input bar with safe area inset */}
      <div className="sticky bottom-0 border-t border-border bg-background pb-[env(safe-area-inset-bottom)]">
        <div className="px-3 py-2">
          <ChatInput
            onSend={handleSend}
            isSubmitting={isSending}
            disabled={isStreaming}
            placeholder={isStreaming ? "Waiting for response..." : "Message your agent..."}
            agents={agents}
            agentsLoading={agentsLoading}
            pendingFiles={pendingFiles}
            onRemoveFile={removeFile}
            onFilesPicked={(files) => files.forEach(addFile)}
            enableVoiceInput={true}
          />
        </div>
      </div>
    </div>
  );
}
