"use client";
import {
  createContext,
  useContext,
  useState,
  useCallback,
  useMemo,
  useRef,
  useEffect,
} from "react";
import Cookies from "js-cookie";
import { onAuthStateChanged } from "firebase/auth";
import { auth } from "@/lib/firebase";
import type { Conversation, Message, OfferStatus } from "@/components/messaging/types";
import { useAppSelector } from "@/store/hooks";
import {
  registerChatUser,
  getChatUser,
  ensureConversation,
  subscribeConversations,
  subscribeMessages,
  sendFirestoreMessage,
  markConversationRead,
  generateConvId,
  type FirestoreConversation,
  type FirestoreChatMessage,
} from "@/lib/chatService";

/* ─── Context shape ──────────────────────────────────────────────────────────── */

export interface StartConvParams {
  sellerChannelId: string;
  sellerFirebaseId?: string;
  sellerName: string;
  sellerAvatar: string;
  listingId: string;
  listingTitle: string;
  listingImage: string;
  listingPrice: number;
}

interface MessagingCtx {
  conversations: Conversation[];
  activeTab: string;
  setActiveTab: (tab: string) => void;
  selectedConversationId: string | null;
  selectConversation: (id: string | null) => void;
  sendMessage: (conversationId: string, content: string, type?: "text" | "offer", offerAmount?: number) => void;
  respondToOffer: (conversationId: string, messageId: string, status: OfferStatus) => void;
  markAsRead: (conversationId: string) => void;
  archiveConversation: (conversationId: string) => void;
  deleteConversation: (conversationId: string) => void;
  blockUser: (conversationId: string) => void;
  typingConversationId: string | null;
  totalUnread: number;
  isLoading: boolean;
  myChannelId: string;
  startConversation: (params: StartConvParams) => Promise<string | null>;
}

const MessagingContext = createContext<MessagingCtx>({
  conversations: [],
  activeTab: "all",
  setActiveTab: () => {},
  selectedConversationId: null,
  selectConversation: () => {},
  sendMessage: () => {},
  respondToOffer: () => {},
  markAsRead: () => {},
  archiveConversation: () => {},
  deleteConversation: () => {},
  blockUser: () => {},
  typingConversationId: null,
  totalUnread: 0,
  isLoading: false,
  myChannelId: "",
  startConversation: async () => null,
});

/* ─── Mapping helpers ────────────────────────────────────────────────────────── */

function mapFirestoreToMessage(
  m: FirestoreChatMessage,
  convId: string,
  myChannelId: string,
): Message {
  return {
    id: m.id,
    conversationId: convId,
    senderId: m.idFrom === myChannelId ? "me" : m.idFrom,
    type: "text",
    content: m.content,
    timestamp: new Date(Number(m.timestamp)).toISOString(),
  };
}

async function mapFirestoreToConversation(
  fsConv: FirestoreConversation,
  myChannelId: string,
): Promise<Conversation> {
  const otherChannelId = fsConv.users.find((u) => u !== myChannelId) ?? "";
  const otherUser = otherChannelId ? await getChatUser(otherChannelId) : null;

  const lastMsgTime = fsConv.lastMessage?.timestamp
    ? new Date(Number(fsConv.lastMessage.timestamp)).toISOString()
    : new Date(0).toISOString();

  const unread =
    fsConv.lastMessage &&
    !fsConv.lastMessage.read &&
    fsConv.lastMessage.idTo === myChannelId
      ? 1
      : 0;

  return {
    id: fsConv.id,
    listingId: fsConv.listingId ?? "",
    listingSlug: fsConv.listingId ?? "",
    listingTitle: fsConv.listingTitle ?? "",
    listingImage: fsConv.listingImage ?? "",
    listingPrice: fsConv.listingPrice ?? 0,
    listingCondition: "good",
    listingCity: "",
    listingStatus: "available",
    otherUserId: otherChannelId,
    otherUserName: otherUser?.name ?? "User",
    otherUserAvatar: otherUser?.profileurl ?? "",
    otherUserVerified: false,
    otherUserIsOnline: otherUser?.isOnline ?? false,
    myRole: fsConv.sellerChannelId === myChannelId ? "seller" : "buyer",
    lastMessage: fsConv.lastMessage?.content ?? "",
    lastMessageTime: lastMsgTime,
    unreadCount: unread,
    isPinned: false,
    isArchived: false,
    isBlocked: false,
    messages: [],
  };
}

/* ─── Provider ───────────────────────────────────────────────────────────────── */

export function MessagingProvider({ children }: { children: React.ReactNode }) {
  const { user, isAuthenticated } = useAppSelector((s) => s.auth);
  const myChannelId = user?.channel_id ?? Cookies.get("channel_id") ?? "";

  const [conversations, setConversations] = useState<Conversation[]>([]);
  const [activeTab, setActiveTab] = useState("all");
  const [selectedConversationId, setSelectedConversationId] = useState<string | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const [firebaseReady, setFirebaseReady] = useState(false);

  const msgUnsubRef = useRef<(() => void) | null>(null);
  const convUnsubRef = useRef<(() => void) | null>(null);

  /* Wait for Firebase Auth to be initialised — it resolves either with the
     existing session (page reload) or null (anonymous / not-yet-signed-in). */
  useEffect(() => {
    const unsub = onAuthStateChanged(auth, () => {
      setFirebaseReady(true);
      unsub(); // only need the first event — session state is now known
    });
    return () => unsub();
  }, []);

  /* Register current user in Firestore so others can look them up */
  useEffect(() => {
    if (!isAuthenticated || !myChannelId || !user || !firebaseReady) return;
    registerChatUser(myChannelId, {
      name: user.full_name ?? "",
      email: user.email ?? "",
      profileurl: user.image ?? "",
      userId: String(user.id),
    }).catch(() => {});
  }, [isAuthenticated, myChannelId, user, firebaseReady]);

  /* Subscribe to all conversations where I'm a participant */
  useEffect(() => {
    if (convUnsubRef.current) {
      convUnsubRef.current();
      convUnsubRef.current = null;
    }
    if (!myChannelId || !firebaseReady) {
      setIsLoading(false);
      return;
    }
    setIsLoading(true);

    const unsub = subscribeConversations(myChannelId, async (fsDocs) => {
      const mapped = await Promise.all(
        fsDocs.map((d) => mapFirestoreToConversation(d, myChannelId)),
      );
      mapped.sort(
        (a, b) =>
          new Date(b.lastMessageTime).getTime() -
          new Date(a.lastMessageTime).getTime(),
      );
      // Preserve messages already loaded by the messages subscription.
      // mapFirestoreToConversation always returns messages:[] because messages
      // live in a subcollection — without this merge, every conversation-level
      // Firestore update (e.g. lastMessage change after a send) would wipe the
      // messages that subscribeMessages had already populated.
      setConversations((prev) =>
        mapped.map((newConv) => {
          const existing = prev.find((c) => c.id === newConv.id);
          return existing ? { ...newConv, messages: existing.messages } : newConv;
        }),
      );
      setIsLoading(false);
    });

    convUnsubRef.current = unsub;
    return () => {
      unsub();
      convUnsubRef.current = null;
    };
  }, [myChannelId, firebaseReady]);

  /* Subscribe to messages for the active conversation */
  useEffect(() => {
    if (msgUnsubRef.current) {
      msgUnsubRef.current();
      msgUnsubRef.current = null;
    }
    if (!selectedConversationId || !myChannelId) return;

    const unsub = subscribeMessages(selectedConversationId, 50, (fsMsgs) => {
      const messages: Message[] = fsMsgs.map((m) =>
        mapFirestoreToMessage(m, selectedConversationId, myChannelId),
      );
      setConversations((prev) =>
        prev.map((c) =>
          c.id === selectedConversationId ? { ...c, messages } : c,
        ),
      );
    });

    msgUnsubRef.current = unsub;
    return () => {
      if (msgUnsubRef.current) {
        msgUnsubRef.current();
        msgUnsubRef.current = null;
      }
    };
  }, [selectedConversationId, myChannelId]);

  /* ─── Actions ─────────────────────────────────────────────────────────────── */

  const selectConversation = useCallback(
    (id: string | null) => {
      setSelectedConversationId(id);
      if (id && myChannelId) {
        markConversationRead(id, myChannelId).catch(() => {});
        setConversations((prev) =>
          prev.map((c) => (c.id === id ? { ...c, unreadCount: 0 } : c)),
        );
      }
    },
    [myChannelId],
  );

  const sendMessage = useCallback(
    (
      conversationId: string,
      content: string,
      _type: "text" | "offer" = "text",
      _offerAmount?: number,
    ) => {
      if (!myChannelId || !content.trim()) return;
      const conv = conversations.find((c) => c.id === conversationId);
      if (!conv) return;
      sendFirestoreMessage(content, conversationId, myChannelId, conv.otherUserId).catch(
        () => {},
      );
    },
    [conversations, myChannelId],
  );

  const respondToOffer = useCallback(
    (_conversationId: string, _messageId: string, _status: OfferStatus) => {
      // Offer flow: local-only for now
    },
    [],
  );

  const markAsRead = useCallback(
    (conversationId: string) => {
      if (myChannelId) {
        markConversationRead(conversationId, myChannelId).catch(() => {});
      }
      setConversations((prev) =>
        prev.map((c) => (c.id === conversationId ? { ...c, unreadCount: 0 } : c)),
      );
    },
    [myChannelId],
  );

  const archiveConversation = useCallback((conversationId: string) => {
    setConversations((prev) =>
      prev.map((c) =>
        c.id === conversationId ? { ...c, isArchived: !c.isArchived } : c,
      ),
    );
  }, []);

  const deleteConversation = useCallback((conversationId: string) => {
    setConversations((prev) => prev.filter((c) => c.id !== conversationId));
    setSelectedConversationId((prev) =>
      prev === conversationId ? null : prev,
    );
  }, []);

  const blockUser = useCallback((conversationId: string) => {
    setConversations((prev) =>
      prev.map((c) =>
        c.id === conversationId ? { ...c, isBlocked: !c.isBlocked } : c,
      ),
    );
  }, []);

  const startConversation = useCallback(
    async (params: StartConvParams): Promise<string | null> => {
      console.log("[startConversation] called", { myChannelId, isAuthenticated, params });

      if (!myChannelId || !isAuthenticated) {
        console.warn("[startConversation] early return — myChannelId:", myChannelId, "isAuthenticated:", isAuthenticated);
        return null;
      }
      if (myChannelId === params.sellerChannelId) {
        console.warn("[startConversation] early return — buyer === seller", myChannelId);
        return null;
      }

      const convId = generateConvId(myChannelId, params.sellerChannelId);
      console.log("[startConversation] convId:", convId);

      try {
        const sellerDoc = await getChatUser(params.sellerChannelId);
        console.log("[startConversation] sellerDoc by channel_id:", sellerDoc);
        if (!sellerDoc) {
          await registerChatUser(params.sellerChannelId, {
            name: params.sellerName,
            email: "",
            profileurl: params.sellerAvatar,
            userId: params.sellerFirebaseId ?? "",
          });
          console.log("[startConversation] registered seller by channel_id");
        }

        if (params.sellerFirebaseId && params.sellerFirebaseId !== params.sellerChannelId) {
          await registerChatUser(params.sellerFirebaseId, {
            name: params.sellerName,
            email: "",
            profileurl: params.sellerAvatar,
            userId: params.sellerFirebaseId,
          });
          console.log("[startConversation] registered seller by firebase_id:", params.sellerFirebaseId);
        }

        await ensureConversation(convId, myChannelId, params.sellerChannelId, {
          id: params.listingId,
          title: params.listingTitle,
          image: params.listingImage,
          price: params.listingPrice,
          sellerChannelId: params.sellerChannelId,
        });
        console.log("[startConversation] conversation ensured, returning convId:", convId);
      } catch (err) {
        console.error("[startConversation] Firestore error:", err);
        throw err;
      }

      return convId;
    },
    [myChannelId, isAuthenticated],
  );

  const totalUnread = useMemo(
    () => conversations.reduce((sum, c) => sum + c.unreadCount, 0),
    [conversations],
  );

  const value = useMemo(
    () => ({
      conversations,
      activeTab,
      setActiveTab,
      selectedConversationId,
      selectConversation,
      sendMessage,
      respondToOffer,
      markAsRead,
      archiveConversation,
      deleteConversation,
      blockUser,
      typingConversationId: null,
      totalUnread,
      isLoading,
      myChannelId,
      startConversation,
    }),
    [
      conversations,
      activeTab,
      selectedConversationId,
      selectConversation,
      sendMessage,
      respondToOffer,
      markAsRead,
      archiveConversation,
      deleteConversation,
      blockUser,
      totalUnread,
      isLoading,
      myChannelId,
      startConversation,
    ],
  );

  return (
    <MessagingContext.Provider value={value}>
      {children}
    </MessagingContext.Provider>
  );
}

export const useMessaging = () => useContext(MessagingContext);
