import { useState, useEffect, useCallback } from "react";
import { supabase } from "@/integrations/supabase/client";
import { useAuth } from "@/contexts/AuthContext";

export interface DirectMessage {
  id: string;
  sender_id: string;
  recipient_id: string;
  message: string;
  read_at: string | null;
  created_at: string;
}

export interface Conversation {
  partner_id: string;
  partner_name: string;
  partner_avatar: string | null;
  partner_unit: string | null;
  partner_owner_name: string | null;
  last_message: string;
  last_message_at: string;
  unread_count: number;
}

export const STAFF_GROUP_ID = "__avria_staff__";
export const STAFF_GROUP_NAME = "Avria Community Management";

type StaffProfile = { user_id: string; full_name: string | null; avatar_url: string | null };

async function getSenderDisplayName(userId: string, fallback?: string | null) {
  const { data, error } = await supabase
    .from("profiles")
    .select("full_name")
    .eq("user_id", userId)
    .maybeSingle();

  if (error) {
    console.error("Error fetching sender profile:", error);
  }

  return data?.full_name?.trim() || fallback || "Someone";
}

async function notifyMessageRecipients(recipientIds: string[], senderName: string, message: string) {
  const preview = message.trim().replace(/\s+/g, " ").slice(0, 140);

  const notificationResults = await Promise.allSettled(
    recipientIds.map((recipientId) =>
      (supabase as any).rpc("insert_notification", {
        p_user_id: recipientId,
        p_type: "info",
        p_title: `New message from ${senderName}`,
        p_message: preview,
        p_related_item_type: "direct_message",
        p_link: "/dashboard/messages",
      })
    )
  );

  notificationResults.forEach((result) => {
    if (result.status === "rejected") console.error("Message notification failed:", result.reason);
  });

  const { data, error } = await supabase.functions.invoke("send-smtp-email", {
    body: {
      action: "send_direct_message_notification",
      recipient_ids: recipientIds,
      sender_name: senderName,
      message,
    },
  });

  if (error || data?.success === false) {
    console.error("Message notification email failed:", error || data?.failed || data?.error);
  }
}

async function fetchStaffProfiles(currentUserId?: string): Promise<StaffProfile[]> {
  const { data, error } = await (supabase as any).rpc("get_message_staff_profiles", {
    _current_user_id: currentUserId ?? null,
  });

  if (error) {
    console.error("Error fetching staff recipients:", error);
    return [];
  }

  return (data ?? []) as StaffProfile[];
}

function normalizeStaffGroupMessages(messages: DirectMessage[], currentUserId: string) {
  return messages.reduce<DirectMessage[]>((acc, msg) => {
    const previous = acc[acc.length - 1];
    const isDuplicateFanout = previous
      && msg.sender_id === currentUserId
      && previous.sender_id === currentUserId
      && previous.message === msg.message
      && Math.abs(new Date(msg.created_at).getTime() - new Date(previous.created_at).getTime()) < 5000;

    if (!isDuplicateFanout) acc.push(msg);
    return acc;
  }, []);
}

export function useConversations() {
  const { user, isHomeowner, isRvBoatLot, isBoardMember, isStaff } = useAuth();
  const [conversations, setConversations] = useState<Conversation[]>([]);
  const [loading, setLoading] = useState(true);

  const fetchConversations = useCallback(async () => {
    if (!user) return;

    if (isHomeowner || isRvBoatLot) {
      const staffProfiles = await fetchStaffProfiles(user.id);
      const staffIds = staffProfiles.map((p) => p.user_id);

      if (staffIds.length === 0) {
        setConversations([]);
        setLoading(false);
        return;
      }

      const { data: staffMessages, error: staffError } = await supabase
        .from("direct_messages")
        .select("*")
        .or(
          `and(sender_id.eq.${user.id},recipient_id.in.(${staffIds.join(",")})),and(sender_id.in.(${staffIds.join(",")}),recipient_id.eq.${user.id})`
        )
        .order("created_at", { ascending: false });

      if (staffError) {
        console.error("Error fetching staff conversation:", staffError);
        setLoading(false);
        return;
      }

      const messages = normalizeStaffGroupMessages((staffMessages ?? []).reverse() as DirectMessage[], user.id).reverse();
      const latest = messages[0];
      setConversations(latest ? [{
        partner_id: STAFF_GROUP_ID,
        partner_name: STAFF_GROUP_NAME,
        partner_avatar: null,
        partner_unit: null,
        partner_owner_name: null,
        last_message: latest.message,
        last_message_at: latest.created_at,
        unread_count: messages.filter((m) => m.recipient_id === user.id && !m.read_at).length,
      }] : []);
      setLoading(false);
      return;
    }

    const { data: messages, error } = await supabase
      .from("direct_messages")
      .select("*")
      .or(`sender_id.eq.${user.id},recipient_id.eq.${user.id}`)
      .order("created_at", { ascending: false });

    if (error) {
      console.error("Error fetching conversations:", error);
      setLoading(false);
      return;
    }

    // Group by conversation partner
    const convMap = new Map<string, { messages: DirectMessage[] }>();
    for (const msg of (messages ?? []) as DirectMessage[]) {
      const partnerId = msg.sender_id === user.id ? msg.recipient_id : msg.sender_id;
      if (!convMap.has(partnerId)) convMap.set(partnerId, { messages: [] });
      convMap.get(partnerId)!.messages.push(msg);
    }

    // Fetch profiles for all partners
    const partnerIds = Array.from(convMap.keys());
    let profileMap = new Map<string, { full_name: string | null; avatar_url: string | null }>();
    let ownerMap = new Map<string, { name: string; unit: string | null }>();

    if (partnerIds.length > 0) {
      const [{ data: profiles }, { data: owners }] = await Promise.all([
        supabase
          .from("profiles")
          .select("user_id, full_name, avatar_url")
          .in("user_id", partnerIds),
        supabase
          .from("owners")
          .select("user_id, first_name, last_name, property_address, units(address)")
          .in("user_id", partnerIds)
          .neq("status", "archived"),
      ]);

      profileMap = new Map(
        (profiles ?? []).map((p) => [p.user_id, { full_name: p.full_name, avatar_url: p.avatar_url }])
      );

      for (const o of (owners ?? []) as any[]) {
        if (!o.user_id || ownerMap.has(o.user_id)) continue;
        const name = `${o.first_name || ""} ${o.last_name || ""}`.trim();
        const unit = o.property_address || o.units?.address || null;
        ownerMap.set(o.user_id, { name, unit });
      }
    }

    const convList: Conversation[] = partnerIds.map((pid) => {
      const msgs = convMap.get(pid)!.messages;
      const latest = msgs[0];
      const profile = profileMap.get(pid);
      const owner = ownerMap.get(pid);
      const unread = msgs.filter((m) => m.recipient_id === user.id && !m.read_at).length;

      return {
        partner_id: pid,
        partner_name: profile?.full_name || owner?.name || "Unknown User",
        partner_avatar: profile?.avatar_url ?? null,
        partner_owner_name: owner?.name || null,
        partner_unit: owner?.unit ?? null,
        last_message: latest.message,
        last_message_at: latest.created_at,
        unread_count: unread,
      };
    });

    convList.sort((a, b) => new Date(b.last_message_at).getTime() - new Date(a.last_message_at).getTime());

    // Board members (non-staff) also get a pinned "message all management" group entry.
    if (isBoardMember && !isStaff) {
      const staffProfiles = await fetchStaffProfiles(user.id);
      const staffIds = new Set(staffProfiles.map((p) => p.user_id));

      // Pull existing staff-group messages (if any) to show a preview + unread badge.
      let lastMessage = "Message all of management";
      let lastAt = new Date(0).toISOString();
      let unread = 0;
      if (staffIds.size > 0) {
        const idList = Array.from(staffIds).join(",");
        const { data: sm } = await supabase
          .from("direct_messages")
          .select("*")
          .or(
            `and(sender_id.eq.${user.id},recipient_id.in.(${idList})),and(sender_id.in.(${idList}),recipient_id.eq.${user.id})`
          )
          .order("created_at", { ascending: false });
        const normalized = normalizeStaffGroupMessages(((sm ?? []) as DirectMessage[]).reverse(), user.id).reverse();
        const latest = normalized[0];
        if (latest) {
          lastMessage = latest.message;
          lastAt = latest.created_at;
        }
        unread = normalized.filter((m) => m.recipient_id === user.id && !m.read_at).length;
      }

      // Remove any per-admin conversation rows that are already covered by the group entry.
      const deduped = convList.filter((c) => !staffIds.has(c.partner_id));
      deduped.unshift({
        partner_id: STAFF_GROUP_ID,
        partner_name: STAFF_GROUP_NAME,
        partner_avatar: null,
        partner_unit: null,
        partner_owner_name: null,
        last_message: lastMessage,
        last_message_at: lastAt,
        unread_count: unread,
      });
      setConversations(deduped);
    } else {
      setConversations(convList);
    }
    setLoading(false);
  }, [user, isHomeowner, isRvBoatLot, isBoardMember, isStaff]);

  useEffect(() => {
    fetchConversations();

    if (!user) return;

    const channel = supabase
      .channel("dm-conversations")
      .on("postgres_changes", { event: "*", schema: "public", table: "direct_messages" }, () => {
        fetchConversations();
      })
      .subscribe();

    return () => { supabase.removeChannel(channel); };
  }, [fetchConversations, user]);

  return { conversations, loading, refresh: fetchConversations };
}

export interface PartnerInfo {
  ownerName: string | null;
  unit: string | null;
  associationName: string | null;
}

export function usePartnerInfo(partnerId: string | null) {
  const [info, setInfo] = useState<PartnerInfo | null>(null);
  useEffect(() => {
    if (!partnerId || partnerId === STAFF_GROUP_ID) { setInfo(null); return; }
    let cancelled = false;
    (async () => {
      const { data } = await supabase
        .from("owners")
        .select("first_name, last_name, property_address, units(address), associations(name)")
        .eq("user_id", partnerId)
        .neq("status", "archived")
        .limit(1)
        .maybeSingle();
      if (cancelled) return;
      if (!data) { setInfo(null); return; }
      const ownerName = `${(data as any).first_name || ""} ${(data as any).last_name || ""}`.trim() || null;
      const unit = (data as any).property_address || (data as any).units?.address || null;
      const associationName = (data as any).associations?.name || null;
      setInfo({ ownerName, unit, associationName });
    })();
    return () => { cancelled = true; };
  }, [partnerId]);
  return info;
}

export function useChatMessages(partnerId: string | null) {
  const { user } = useAuth();
  const [messages, setMessages] = useState<DirectMessage[]>([]);
  const [loading, setLoading] = useState(true);

  const fetchMessages = useCallback(async () => {
    if (!user || !partnerId) {
      setMessages([]);
      setLoading(false);
      return;
    }

    if (partnerId === STAFF_GROUP_ID) {
      const staffProfiles = await fetchStaffProfiles(user.id);
      const staffIds = staffProfiles.map((p) => p.user_id);

      if (staffIds.length === 0) {
        setMessages([]);
        setLoading(false);
        return;
      }

      const { data, error } = await supabase
        .from("direct_messages")
        .select("*")
        .or(
          `and(sender_id.eq.${user.id},recipient_id.in.(${staffIds.join(",")})),and(sender_id.in.(${staffIds.join(",")}),recipient_id.eq.${user.id})`
        )
        .order("created_at", { ascending: true });

      if (error) console.error("Error fetching staff messages:", error);
      setMessages(normalizeStaffGroupMessages((data ?? []) as DirectMessage[], user.id));
      setLoading(false);

      const unread = (data ?? []).filter((m: any) => m.recipient_id === user.id && !m.read_at);
      if (unread.length > 0) {
        await supabase
          .from("direct_messages")
          .update({ read_at: new Date().toISOString() })
          .in("id", unread.map((m: any) => m.id));
      }
      return;
    }

    const { data, error } = await supabase
      .from("direct_messages")
      .select("*")
      .or(
        `and(sender_id.eq.${user.id},recipient_id.eq.${partnerId}),and(sender_id.eq.${partnerId},recipient_id.eq.${user.id})`
      )
      .order("created_at", { ascending: true });

    if (error) console.error("Error fetching messages:", error);
    setMessages((data ?? []) as DirectMessage[]);
    setLoading(false);

    // Mark unread messages as read
    const unread = (data ?? []).filter(
      (m: any) => m.recipient_id === user.id && !m.read_at
    );
    if (unread.length > 0) {
      await supabase
        .from("direct_messages")
        .update({ read_at: new Date().toISOString() })
        .in("id", unread.map((m: any) => m.id));
    }
  }, [user, partnerId]);

  useEffect(() => {
    fetchMessages();

    if (!user || !partnerId) return;

    const channel = supabase
      .channel(`dm-chat-${partnerId}`)
      .on("postgres_changes", { event: "INSERT", schema: "public", table: "direct_messages" }, (payload) => {
        const msg = payload.new as DirectMessage;
        if (partnerId === STAFF_GROUP_ID) {
          if (msg.sender_id === user.id || msg.recipient_id === user.id) {
            setMessages((prev) => normalizeStaffGroupMessages(
              prev.some((m) => m.id === msg.id) ? prev : [...prev, msg],
              user.id
            ));
            if (msg.recipient_id === user.id && !msg.read_at) {
              supabase
                .from("direct_messages")
                .update({ read_at: new Date().toISOString() })
                .eq("id", msg.id)
                .then();
            }
          }
          return;
        }
        if (
          (msg.sender_id === user.id && msg.recipient_id === partnerId) ||
          (msg.sender_id === partnerId && msg.recipient_id === user.id)
        ) {
          setMessages((prev) => [...prev, msg]);
          // Auto-mark as read if we're the recipient
          if (msg.recipient_id === user.id && !msg.read_at) {
            supabase
              .from("direct_messages")
              .update({ read_at: new Date().toISOString() })
              .eq("id", msg.id)
              .then();
          }
        }
      })
      .subscribe();

    return () => { supabase.removeChannel(channel); };
  }, [fetchMessages, user, partnerId]);

  const sendMessage = useCallback(
    async (text: string) => {
      if (!user || !partnerId || !text.trim()) return;

      const trimmedMessage = text.trim();
      const senderName = await getSenderDisplayName(user.id, user.email);

      if (partnerId === STAFF_GROUP_ID) {
        const staffProfiles = await fetchStaffProfiles(user.id);
        const staffIds = staffProfiles.map((p) => p.user_id);
        if (staffIds.length === 0) throw new Error("No staff recipients found");

        const { error } = await supabase.from("direct_messages").insert(
          staffIds.map((staffId) => ({
            sender_id: user.id,
            recipient_id: staffId,
            message: trimmedMessage,
          }))
        );

        if (error) throw error;
        await notifyMessageRecipients(staffIds, senderName, trimmedMessage);
        return;
      }

      const { error } = await supabase.from("direct_messages").insert({
        sender_id: user.id,
        recipient_id: partnerId,
        message: trimmedMessage,
      });

      if (error) throw error;
      await notifyMessageRecipients([partnerId], senderName, trimmedMessage);
    },
    [user, partnerId]
  );

  const deleteMessage = useCallback(async (messageId: string) => {
    const { error } = await supabase.from("direct_messages").delete().eq("id", messageId);
    if (error) throw error;
    setMessages((prev) => prev.filter((m) => m.id !== messageId));
  }, []);

  return { messages, loading, sendMessage, deleteMessage };
}
