"use client";

import { useEffect, useRef, useState } from "react";
import { useRouter } from "next/navigation";
import toast from "react-hot-toast";

import {
  NotificationAudience,
  NotificationItem,
  getNotificationCount,
  getNotifications,
  markNotificationsRead,
  markNotificationsSeen,
  matchLiveConsultationRequest,
} from "@/app/lib/notifications";
import {
  ConsultationRequest,
  acceptConsultation,
  getConsultationRequests,
  rejectConsultation,
} from "@/app/lib/astrologer";

const COUNT_POLL_MS = 20000;
const REQUEST_POLL_MS = 4000;

function initials(name?: string) {
  return (
    (name || "?")
      .trim()
      .split(/\s+/)
      .slice(0, 2)
      .map((part) => part.charAt(0).toUpperCase())
      .join("") || "?"
  );
}

// Shared bell + preview dropdown for both apps — audience/token/href are
// the only things that differ between the customer header and the
// astrologer sidebar, so this stays a single component rather than two
// near-duplicates. The astrologer-only request-card behaviour below is
// gated on `audience === "astrologer"` and never runs for the customer
// side.
export default function NotificationBell({
  audience,
  token,
  notificationsHref,
  dark,
}: {
  audience: NotificationAudience;
  token: string | null;
  notificationsHref: string;
  dark?: boolean;
}) {
  const router = useRouter();
  const wrapperRef = useRef<HTMLDivElement>(null);

  const [open, setOpen] = useState(false);
  const [unseen, setUnseen] = useState(0);
  const [items, setItems] = useState<NotificationItem[]>([]);
  const [loading, setLoading] = useState(false);

  // Same trick as the full astrologer notifications page: don't trust an
  // unconfirmed notification_type code for "this is a live request" —
  // cross-reference chat_request_id/call_request_id against the real
  // ring queue instead, and only render Accept/Reject while it's
  // actually still there.
  const [openRequests, setOpenRequests] = useState<Map<number, ConsultationRequest>>(
    new Map(),
  );
  const [requestSeconds, setRequestSeconds] = useState<Record<number, number>>({});
  const [actingId, setActingId] = useState<number | null>(null);

  useEffect(() => {
    if (!token) return;

    let cancelled = false;

    const poll = async () => {
      const counts = await getNotificationCount(audience, token);
      if (!cancelled) setUnseen(counts.unseen ?? 0);
    };

    poll();
    const interval = setInterval(poll, COUNT_POLL_MS);

    return () => {
      cancelled = true;
      clearInterval(interval);
    };
  }, [audience, token]);

  // Ring-queue poll only runs for the astrologer bell, and only while
  // the panel is actually open — every mounted instance of this bell
  // (dashboard, orders, wallet, profile all carry one) would otherwise
  // poll in the background for no reason.
  useEffect(() => {
    if (audience !== "astrologer" || !token || !open) return;

    let cancelled = false;

    const poll = async () => {
      let response;

      try {
        response = await getConsultationRequests(token);
      } catch (error) {
        console.error("Ring queue poll (notification bell) failed:", error);
        return;
      }

      if (cancelled || !response?.success) return;

      const nextItems: ConsultationRequest[] = response.data?.items ?? response.items ?? [];

      setOpenRequests(new Map(nextItems.map((r) => [r.consultation_id, r])));

      setRequestSeconds((previous) => {
        const next = { ...previous };
        nextItems.forEach((r) => {
          if (r.seconds_to_expiry != null) next[r.consultation_id] = Number(r.seconds_to_expiry);
        });
        return next;
      });
    };

    poll();
    const interval = setInterval(poll, REQUEST_POLL_MS);

    return () => {
      cancelled = true;
      clearInterval(interval);
    };
  }, [audience, token, open]);

  useEffect(() => {
    if (audience !== "astrologer" || !open) return;

    const interval = setInterval(() => {
      setRequestSeconds((previous) => {
        const next: Record<number, number> = {};
        Object.entries(previous).forEach(([id, seconds]) => {
          next[Number(id)] = Math.max(0, seconds - 1);
        });
        return next;
      });
    }, 1000);

    return () => clearInterval(interval);
  }, [audience, open]);

  useEffect(() => {
    function onClickOutside(event: MouseEvent) {
      if (wrapperRef.current && !wrapperRef.current.contains(event.target as Node)) {
        setOpen(false);
      }
    }

    document.addEventListener("mousedown", onClickOutside);
    return () => document.removeEventListener("mousedown", onClickOutside);
  }, []);

  const togglePanel = async () => {
    const next = !open;
    setOpen(next);

    if (!next || !token) return;

    setLoading(true);
    const result = await getNotifications(audience, token, { limit: 6 });
    setItems(result.list);
    setLoading(false);

    // Clears the tab dot without marking anything read — entries still
    // render unread until individually opened.
    markNotificationsSeen(audience, token);
    setUnseen(0);
  };

  const openItem = (item: NotificationItem) => {
    setOpen(false);

    if (!item.is_read && token) {
      markNotificationsRead(audience, token, { ids: [item.id] });
    }

    router.push(notificationsHref);
  };

  // Fresh per render — stops the same still-open request being attached
  // to more than one historical notification about the same customer.
  const claimedRequestIds = new Set<number>();

  const getLiveRequest = (item: NotificationItem): ConsultationRequest | undefined => {
    if (audience !== "astrologer") return undefined;
    return matchLiveConsultationRequest(item, openRequests, claimedRequestIds);
  };

  const handleAccept = async (request: ConsultationRequest) => {
    if (!token) return;

    try {
      setActingId(request.consultation_id);
      const response = await acceptConsultation(request.consultation_id, token, request);

      if (!response.success) {
        const silent = [
          "this consultation is no longer available",
          "you are already in a consultation",
          "the customer no longer has sufficient balance",
        ];

        if (!silent.some((m) => (response.message || "").toLowerCase().includes(m))) {
          toast.error(response.message || "Unable to accept this request.");
        }

        setOpenRequests((prev) => {
          const next = new Map(prev);
          next.delete(request.consultation_id);
          return next;
        });
        return;
      }

      setOpen(false);
      router.push(`/astrologer/consultation/${request.consultation_id}`);
    } finally {
      setActingId(null);
    }
  };

  const handleReject = async (request: ConsultationRequest) => {
    if (!token) return;

    try {
      setActingId(request.consultation_id);
      await rejectConsultation(request.consultation_id, "Not available right now", token);

      setOpenRequests((prev) => {
        const next = new Map(prev);
        next.delete(request.consultation_id);
        return next;
      });
    } finally {
      setActingId(null);
    }
  };

  return (
    <div className={`notif-bell ${dark ? "notif-bell-dark" : ""}`} ref={wrapperRef}>
      <button
        type="button"
        className="icon-btn notif-bell-btn"
        onClick={togglePanel}
        aria-label="Notifications"
      >
        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none">
          <path
            d="M12 2a6 6 0 0 0-6 6v3.09c0 .56-.22 1.1-.62 1.5L4 14v1h16v-1l-1.38-1.41a2.13 2.13 0 0 1-.62-1.5V8a6 6 0 0 0-6-6Z"
            fill="currentColor"
          />
          <path
            d="M9.5 18a2.5 2.5 0 0 0 5 0"
            stroke="currentColor"
            strokeWidth="1.6"
            strokeLinecap="round"
          />
        </svg>

        {unseen > 0 && (
          <span className="notif-bell-badge">{unseen > 9 ? "9+" : unseen}</span>
        )}
      </button>

      {open && (
        <div className="notif-bell-panel">
          <div className="notif-bell-panel-head">
            <span>Notifications</span>
            <button type="button" onClick={() => openItem({ id: 0, is_read: true } as NotificationItem)}>
              View all
            </button>
          </div>

          {loading ? (
            <div className="notif-bell-empty">Loading...</div>
          ) : items.length === 0 ? (
            <div className="notif-bell-empty">No notifications yet.</div>
          ) : (
            <ul className="notif-bell-list">
              {items.map((item) => {
                const liveRequest = getLiveRequest(item);

                if (liveRequest) {
                  const seconds =
                    requestSeconds[liveRequest.consultation_id] ??
                    liveRequest.seconds_to_expiry ??
                    0;
                  const displayName =
                    liveRequest.consultee_name || liveRequest.user_name || "User";
                  const acting = actingId === liveRequest.consultation_id;

                  return (
                    <li key={item.id} className="notif-bell-request-card">
                      <div className="notif-bell-request-top">
                        <div className="notif-bell-request-avatar">{initials(displayName)}</div>

                        <div className="flex-fill">
                          <div className="notif-bell-request-name">{displayName}</div>
                          <div className="notif-bell-request-meta">
                            {liveRequest.medium === "CHAT" ? "💬 Chat" : "📞 Call"}
                            {liveRequest.is_free_chat
                              ? ` · Free ${liveRequest.free_minutes ?? 0} min`
                              : liveRequest.rate_per_minute
                                ? ` · ₹${liveRequest.rate_per_minute}/min`
                                : ""}
                          </div>
                        </div>

                        <div className={`waiting-expiry ${seconds <= 10 ? "danger" : ""}`}>
                          {seconds}s
                        </div>
                      </div>

                      <div className="notif-bell-request-actions">
                        <button
                          type="button"
                          className="waiting-decline-btn"
                          disabled={acting}
                          onClick={() => handleReject(liveRequest)}
                        >
                          {acting ? "..." : "Reject"}
                        </button>
                        <button
                          type="button"
                          className="waiting-accept-btn"
                          disabled={acting}
                          onClick={() => handleAccept(liveRequest)}
                        >
                          {acting ? "Accepting..." : "Accept"}
                        </button>
                      </div>
                    </li>
                  );
                }

                return (
                  <li
                    key={item.id}
                    className={`notif-bell-item ${item.is_read ? "" : "unread"}`}
                    onClick={() => openItem(item)}
                  >
                    <div className="notif-bell-item-title">{item.title}</div>
                    <div className="notif-bell-item-desc">{item.description}</div>
                    <div className="notif-bell-item-time">{item.time_ago}</div>
                  </li>
                );
              })}
            </ul>
          )}
        </div>
      )}
    </div>
  );
}
