import { db } from "@/lib/firebase";
import {
  doc,
  setDoc,
  getDoc,
  getDocs,
  updateDoc,
  collection,
  query,
  where,
  orderBy,
  limit as fsLimit,
  onSnapshot,
  enableNetwork,
} from "firebase/firestore";

const USERS_COL = "users_doliplay";
const CHATROOMS_COL = "chatrooms_doliplay";

/* ─── Offline retry ──────────────────────────────────────────────────────────── */
// If Firestore hasn't connected yet it throws "client is offline".
// Re-enable the network and retry once before surfacing the error.
async function withOnline<T>(fn: () => Promise<T>): Promise<T> {
  try {
    return await fn();
  } catch (err: unknown) {
    const msg = err instanceof Error ? err.message : "";
    if (msg.includes("offline") || msg.includes("client is offline")) {
      await enableNetwork(db);
      // Give Firestore a moment to establish the connection before retrying
      await new Promise((r) => setTimeout(r, 800));
      return await fn();
    }
    throw err;
  }
}

/* ─── Firestore document shapes ─────────────────────────────────────────────── */

export interface ChatUserDoc {
  channel_id: string;
  user_id: string;
  name: string;
  email: string;
  profileurl: string;
  isOnline: boolean;
  createdAt: string;
}

export interface FirestoreChatMessage {
  id: string;
  idFrom: string;   // sender channel_id
  idTo: string;     // recipient channel_id
  timestamp: string; // epoch ms as string — used as doc ID too
  content: string;
  type: number;      // 0 = text
  read: boolean;
}

export interface FirestoreConversation {
  id: string;
  users: string[];           // [channelId_A, channelId_B]
  listingId: string;
  listingTitle: string;
  listingImage: string;
  listingPrice: number;
  sellerChannelId: string;   // identifies which user is the seller
  lastMessage: {
    content: string;
    idFrom: string;
    idTo: string;
    read: boolean;
    timestamp: string;
  } | null;
}

/* ─── ID helpers ─────────────────────────────────────────────────────────────── */

export function generateConvId(cid1: string, cid2: string): string {
  return cid1 > cid2 ? `${cid1}-${cid2}` : `${cid2}-${cid1}`;
}

/* ─── User operations ───────────────────────────────────────────────────────── */

export async function registerChatUser(
  channelId: string,
  data: { name: string; email: string; profileurl: string; userId: string },
): Promise<void> {
  if (!channelId) return;
  await withOnline(async () => {
    const ref = doc(db, USERS_COL, channelId);
    const snap = await getDoc(ref);
    if (!snap.exists()) {
      await setDoc(ref, {
        channel_id: channelId,
        user_id: data.userId,
        name: data.name,
        email: data.email,
        profileurl: data.profileurl,
        isOnline: true,
        createdAt: Date.now().toString(),
      });
    }
  });
}

export async function getChatUser(channelId: string): Promise<ChatUserDoc | null> {
  if (!channelId) return null;
  return withOnline(async () => {
    const snap = await getDoc(doc(db, USERS_COL, channelId));
    return snap.exists() ? (snap.data() as ChatUserDoc) : null;
  });
}

/* ─── Conversation operations ───────────────────────────────────────────────── */

export async function ensureConversation(
  convId: string,
  myChannelId: string,
  otherChannelId: string,
  listing: {
    id: string;
    title: string;
    image: string;
    price: number;
    sellerChannelId: string;
  },
): Promise<void> {
  await withOnline(async () => {
    const ref = doc(db, CHATROOMS_COL, convId);
    const snap = await getDoc(ref);
    if (!snap.exists()) {
      await setDoc(ref, {
        convid: convId,
        users: [myChannelId, otherChannelId],
        listingId: listing.id,
        listingTitle: listing.title,
        listingImage: listing.image,
        listingPrice: listing.price,
        sellerChannelId: listing.sellerChannelId,
        lastMessage: null,
      });
    }
  });
}

export function subscribeConversations(
  myChannelId: string,
  callback: (convs: FirestoreConversation[]) => void,
): () => void {
  const q = query(
    collection(db, CHATROOMS_COL),
    where("users", "array-contains", myChannelId),
  );
  return onSnapshot(q, (snap) => {
    const docs = snap.docs.map((d) => ({
      id: d.id,
      ...(d.data() as Omit<FirestoreConversation, "id">),
    }));
    callback(docs);
  });
}

/* ─── Message operations ────────────────────────────────────────────────────── */

export function subscribeMessages(
  convId: string,
  msgLimit: number,
  callback: (msgs: FirestoreChatMessage[]) => void,
): () => void {
  const q = query(
    collection(db, CHATROOMS_COL, convId, convId),
    orderBy("timestamp", "desc"),
    fsLimit(msgLimit),
  );
  return onSnapshot(q, (snap) => {
    const msgs = snap.docs
      .map((d) => ({ id: d.id, ...(d.data() as Omit<FirestoreChatMessage, "id">) }))
      .reverse(); // oldest first for display
    callback(msgs);
  });
}

export async function sendFirestoreMessage(
  content: string,
  convId: string,
  myChannelId: string,
  toChannelId: string,
): Promise<void> {
  if (!content.trim() || !convId || !myChannelId || !toChannelId) return;
  const ts = String(Date.now());
  const msgData: Omit<FirestoreChatMessage, "id"> = {
    idFrom: myChannelId,
    idTo: toChannelId,
    timestamp: ts,
    content: content.trim(),
    type: 0,
    read: false,
  };
  await setDoc(doc(db, CHATROOMS_COL, convId, convId, ts), msgData);
  await updateDoc(doc(db, CHATROOMS_COL, convId), { lastMessage: msgData });
}

export async function markConversationRead(
  convId: string,
  myChannelId: string,
): Promise<void> {
  const q = query(
    collection(db, CHATROOMS_COL, convId, convId),
    where("idTo", "==", myChannelId),
    where("read", "==", false),
  );
  const snap = await getDocs(q);
  await Promise.all(
    snap.docs.map((d) =>
      updateDoc(doc(db, CHATROOMS_COL, convId, convId, d.id), { read: true }),
    ),
  );
  try {
    await updateDoc(doc(db, CHATROOMS_COL, convId), {
      "lastMessage.read": true,
    });
  } catch {
    // conversation doc may not exist yet — safe to ignore
  }
}
