"use client";

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

import DashboardSidebar from "@/app/components/astrologer/DashboardSidebar";
import { useVoiceTyping } from "@/app/lib/useVoiceTyping";
import {
  getAstrologerActiveConsultation,
  endAstrologerConsultation,
  getCachedConsultationCustomerName,
  getCachedConsultationCustomerDetails,
  cacheConsultationCustomerDetails,
  extractCustomerDetails,
  CachedCustomerDetails,
  getAstrologerConsultationMessages,
  syncAstrologerConsultationMessages,
} from "@/app/lib/astrologer";
import KundliModal from "@/app/components/consultation/KundliModal";
import {
  ConsultationMessage,
  parseAutoDetailsMessage,
} from "@/app/lib/consultation";
import {
  createAndLoginRtm,
  subscribeRtmChannel,
  onRtmMessage,
  publishRtmMessage,
  logoutRtm,
  joinRtcAudio,
  leaveRtcAudio,
  toggleMic,
  RtcAudioSession,
  mintAgoraChatToken,
  loginAgoraChat,
  logoutAgoraChat,
  onAgoraChatAttachment,
  loadAgoraChatAttachmentHistory,
  sendAgoraChatImage,
  sendAgoraChatAudio,
  ChatSession,
} from "@/app/lib/agora";

// Confirmed live: read-only on the astrologer side (never bills/closes
// a session as a side effect, unlike the customer's /active) — safe
// to poll on a timer while waiting/live, unlike the customer version.
const POLL_MS = 4000;

type Phase = "loading" | "ACCEPTED" | "ONGOING" | "ENDED" | "NONE";

interface ChatBubble {
  clientMessageId: string;
  senderType: "USER" | "ASTROLOGER";
  body: string;
  sentAt: string;
  // Image/voice messages ride a separate Agora Chat connection (see
  // app/lib/agora.ts) alongside the RTM text channel above.
  attachment?: {
    kind: "image" | "audio";
    url: string;
    thumbUrl?: string;
    durationSeconds?: number;
  };
}

// Canned starter phrases — fully local, no API behind them. Clicking
// one appends it to the composer rather than auto-sending, so a
// misclick never fires off a message on the astrologer's behalf.
// Translated by hand for each language in LANGUAGES below (there's no
// chat-translation endpoint anywhere in this app — see LANGUAGES —
// so "Choose language" only changes which of these fixed local sets
// is shown, not a live translation of anything typed or received).
const QUICK_REPLIES_BY_LANGUAGE: Record<string, string[]> = {
  eng: [
    "Please share your birth details",
    "One moment, checking...",
    "Would you like a detailed analysis?",
    "Any other question?",
  ],
  hindi: [
    "कृपया अपनी जन्म तिथि साझा करें",
    "एक क्षण रुकिए, देख रहा हूँ...",
    "क्या आप विस्तृत विश्लेषण चाहेंगे?",
    "कोई और प्रश्न?",
  ],
  marathi: [
    "कृपया तुमची जन्मतारीख शेअर करा",
    "एक क्षण थांबा, तपासत आहे...",
    "तुम्हाला सविस्तर विश्लेषण हवे आहे का?",
    "आणखी काही प्रश्न आहे का?",
  ],
  telugu: [
    "దయచేసి మీ జన్మ వివరాలను పంచుకోండి",
    "ఒక్క క్షణం, చూస్తున్నాను...",
    "మీకు వివరణాత్మక విశ్లేషణ కావాలా?",
    "ఇంకా ఏమైనా ప్రశ్న ఉందా?",
  ],
  kannada: [
    "ದಯವಿಟ್ಟು ನಿಮ್ಮ ಜನ್ಮ ವಿವರಗಳನ್ನು ಹಂಚಿಕೊಳ್ಳಿ",
    "ಒಂದು ಕ್ಷಣ, ಪರಿಶೀಲಿಸುತ್ತಿದ್ದೇನೆ...",
    "ನಿಮಗೆ ವಿವರವಾದ ವಿಶ್ಲೇಷಣೆ ಬೇಕೇ?",
    "ಬೇರೆ ಯಾವುದಾದರೂ ಪ್ರಶ್ನೆ ಇದೆಯೇ?",
  ],
  bengali: [
    "অনুগ্রহ করে আপনার জন্ম বিবরণ শেয়ার করুন",
    "একটু অপেক্ষা করুন, দেখছি...",
    "আপনি কি বিস্তারিত বিশ্লেষণ চান?",
    "আর কোনো প্রশ্ন আছে?",
  ],
  tamil: [
    "தயவுசெய்து உங்கள் பிறப்பு விவரங்களைப் பகிரவும்",
    "ஒரு நிமிடம், பார்க்கிறேன்...",
    "விரிவான பகுப்பாய்வு வேண்டுமா?",
    "வேறு ஏதேனும் கேள்வி உள்ளதா?",
  ],
};

// The composer's "Choose language" picker — this is a purely local
// preference for which QUICK_REPLIES_BY_LANGUAGE set to show, not a
// real backend feature (no session-language/translation endpoint
// exists anywhere in this app; the astrologer's own "languages I
// speak" profile field is a separate, one-time registration setting).
const LANGUAGES = [
  { id: "eng", short: "Eng", native: "English" },
  { id: "hindi", short: "हिंदी", native: "Hindi" },
  { id: "marathi", short: "मराठी", native: "Marathi" },
  { id: "telugu", short: "తెలుగు", native: "Telugu" },
  { id: "kannada", short: "ಕನ್ನಡ", native: "Kannada" },
  { id: "bengali", short: "বাংলা", native: "Bengali" },
  { id: "tamil", short: "தமிழ்", native: "Tamil" },
];

// A shared "Link" message (see handleSendLink) is just a normal text
// message whose whole body is a URL — this is how it's told apart
// from ordinary text so it renders as a clickable chip instead.
function isLikelyUrl(body: string): boolean {
  return /^https?:\/\/\S+$/i.test(body.trim());
}

function secondsUntil(isoLocal?: string): number {
  if (!isoLocal) return 0;
  const target = new Date(isoLocal.replace(" ", "T")).getTime();
  return Math.max(0, Math.round((target - Date.now()) / 1000));
}

export default function AstrologerConsultationPage() {
  const router = useRouter();
  const params = useParams<{ id: string }>();
  const consultationId = Number(params?.id);

  const [phase, setPhase] = useState<Phase>("loading");
  const [userName, setUserName] = useState("");
  const [customerDetails, setCustomerDetails] = useState<CachedCustomerDetails>({});
  const [showKundliModal, setShowKundliModal] = useState(false);
  const [medium, setMedium] = useState<string | undefined>();
  const [joinSecondsLeft, setJoinSecondsLeft] = useState(0);
  const [messages, setMessages] = useState<ChatBubble[]>([]);
  const [composerValue, setComposerValue] = useState("");
  const [showAttachmentSheet, setShowAttachmentSheet] = useState(false);
  const [showLinkInput, setShowLinkInput] = useState(false);
  const [linkDraft, setLinkDraft] = useState("");
  const [sendingImage, setSendingImage] = useState(false);
  const [isRecordingVoice, setIsRecordingVoice] = useState(false);
  const [recordingSeconds, setRecordingSeconds] = useState(0);
  const [sendingVoice, setSendingVoice] = useState(false);
  const [loadingAttachmentHistory, setLoadingAttachmentHistory] = useState(false);
  const [showUserDetailModal, setShowUserDetailModal] = useState(false);
  const [showLanguageModal, setShowLanguageModal] = useState(false);
  const [showPreviousOrdersModal, setShowPreviousOrdersModal] = useState(false);
  const [replyLanguage, setReplyLanguage] = useState("eng");
  const [muted, setMuted] = useState(false);
  const [busy, setBusy] = useState(false);
  const [showEndConfirm, setShowEndConfirm] = useState(false);
  const [ratePerMinute, setRatePerMinute] = useState<number | null>(null);
  const [elapsedSeconds, setElapsedSeconds] = useState(0);

  const tokenRef = useRef<string | null>(null);
  const phaseRef = useRef<Phase>("loading");
  const rtmClientRef = useRef<any>(null);
  const rtmUnsubscribeRef = useRef<(() => void) | null>(null);
  const rtcSessionRef = useRef<RtcAudioSession | null>(null);
  const channelNameRef = useRef<string | null>(null);
  const myRtmAccountRef = useRef<string | null>(null);
  const sequenceRef = useRef(0);
  const pendingSyncRef = useRef<ConsultationMessage[]>([]);
  const joinedAgoraRef = useRef(false);
  const chatSessionRef = useRef<ChatSession | null>(null);
  const chatUnsubscribeRef = useRef<(() => void) | null>(null);
  const imageInputRef = useRef<HTMLInputElement | null>(null);
  const mediaRecorderRef = useRef<MediaRecorder | null>(null);
  const recordedChunksRef = useRef<Blob[]>([]);
  const recordingStreamRef = useRef<MediaStream | null>(null);
  const recordingIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null);

  // poll() below is created once per consultationId and reused by
  // setInterval — reading `phase` state directly inside it would close
  // over whatever value it had at mount (always "loading"), silently
  // breaking the ONGOING/ACCEPTED -> ENDED transition on any later
  // has_active:false read. Read the always-current value via this ref
  // instead (confirmed live: this bug could turn a real ongoing session
  // into a false "This consultation is no longer active.").
  useEffect(() => {
    phaseRef.current = phase;
  }, [phase]);

  useEffect(() => {
    const token = localStorage.getItem("astrologerToken");

    if (!token) {
      router.replace("/astrologer/signin");
      return;
    }

    tokenRef.current = token;

    if (!consultationId) {
      setPhase("NONE");
      return;
    }

    let cancelled = false;

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

      try {
        response = await getAstrologerActiveConsultation(token);
      } catch (error) {
        // A thrown request (network error, or axios rejecting a non-2xx
        // status) would otherwise silently skip every check below and
        // leave the page stuck wherever it was — log it so a "why is
        // this stuck" report has something to check in devtools.
        console.error("Astrologer active-consultation poll failed:", error);
        return;
      }

      if (cancelled) return;

      const data = response?.data ?? response;

      // Confirmed live: this endpoint has NO has_active field at all —
      // "nothing active" and "something active" both come back with
      // status:true and the exact same message ("Active consultation").
      // The real signal is consultation_id being non-zero (0 with
      // status:"" when empty; a real id + status otherwise). The
      // has_active check that used to live here was copied from the
      // *customer* /active shape, which does document has_active — that
      // assumption never held for the astrologer endpoint, and was the
      // root cause of the astrologer's chat window never opening after
      // a real accept (this branch fired unconditionally on every poll).
      const hasActive = !!data?.consultation_id;

      if (!response?.success || !hasActive) {
        console.warn("Astrologer /active reported no active session:", response);

        if (phaseRef.current === "ONGOING" || phaseRef.current === "ACCEPTED") {
          // Flush any still-batched outgoing messages before tearing
          // down — confirmed live this path (the session ending from
          // the customer's side: timeout, insufficient balance, a
          // manual end on their end) never used to flush, unlike the
          // astrologer's own handleEnd() below, so an astrologer reply
          // sent just before the customer's side ended it could be
          // silently lost. Mirrors the customer page's finishSession(),
          // which already flushes on both its manual and auto-detected
          // end paths.
          await flushMessages(true);
          teardownAgora();
          setPhase("ENDED");
        } else if (phaseRef.current === "loading") {
          setPhase("NONE");
        }
        return;
      }

      // Confirmed live: /active never carries a customer-name field
      // while ONGOING (only accept's response does) — fall back to what
      // acceptConsultation() cached at accept time rather than showing
      // a blank "?" avatar for the whole call.
      setUserName(
        data.user_name ||
          data.consultee_name ||
          getCachedConsultationCustomerName(consultationId) ||
          "",
      );
      setMedium(data.medium);

      // Same "prefer live, fall back to what accept() cached" pattern
      // as userName above — unconfirmed whether /active ever carries
      // gender/birth details/marital status/topic itself, so this is
      // defensive either way.
      setCustomerDetails((prev) => {
        const fromActive = extractCustomerDetails(data);
        const cached = getCachedConsultationCustomerDetails(consultationId);
        const merged = { ...cached, ...fromActive };
        return Object.values(merged).some(Boolean) ? merged : prev;
      });

      // Not confirmed present on every real /active response — render
      // the live earnings line only when the backend actually sends it,
      // rather than guessing a value.
      const rate = data.rate_per_minute ?? data.ratePerMinute;
      if (typeof rate === "number") setRatePerMinute(rate);

      if (data.status === "ONGOING" && data.agora && !joinedAgoraRef.current) {
        joinedAgoraRef.current = true;
        // Confirmed live: /active's ONGOING shape carries a real
        // elapsed_seconds field (e.g. 3937 for a call already ~65min
        // in) — seed the local timer from it so resuming a session
        // after a refresh continues from the real duration instead of
        // restarting the on-screen clock at 00:00. A genuinely fresh
        // accept just reads back a small number here (server startup
        // lag), which is fine.
        if (typeof data.elapsed_seconds === "number") {
          setElapsedSeconds(data.elapsed_seconds);
        }
        setPhase("ONGOING");
        await joinAgora(data.agora, data.channel_name, data.medium);
        loadPriorMessages(consultationId, token);

        // The customer's auto-details message can legitimately still
        // be mid-flight the instant this fires (accept happens before
        // the customer even sees the "Start Chat" confirm button) —
        // one retry a few seconds later catches it without needing to
        // poll indefinitely.
        setTimeout(() => loadPriorMessages(consultationId, token), 4000);
      } else if (data.status === "ACCEPTED") {
        setPhase("ACCEPTED");
        // Prefer the server's own pre-computed value — device/server
        // clock skew makes ring/join countdowns computed from a raw
        // timestamp unreliable (confirmed live on the customer side).
        setJoinSecondsLeft(data.seconds_to_join_end ?? secondsUntil(data.join_expires_at));
      }
    };

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

    return () => {
      cancelled = true;
      clearInterval(interval);
      teardownAgora();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [consultationId]);

  useEffect(() => {
    if (phase !== "ACCEPTED") return;
    const interval = setInterval(() => setJoinSecondsLeft((s) => Math.max(0, s - 1)), 1000);
    return () => clearInterval(interval);
  }, [phase]);

  // The astrologer app never ticks (per the handbook, only the customer
  // side does) — this is a local approximation for display only, not an
  // authoritative figure. Deliberately does NOT reset to 0 here — the
  // poll loop above already seeds elapsedSeconds from the real
  // elapsed_seconds on first observing ONGOING (0-ish for a fresh
  // accept, the real in-progress duration when resuming after a
  // refresh) — this effect only needs to keep it ticking forward.
  useEffect(() => {
    if (phase !== "ONGOING") return;
    const interval = setInterval(() => setElapsedSeconds((s) => s + 1), 1000);
    return () => clearInterval(interval);
  }, [phase]);

  // The live message list otherwise only ever grows from RTM events +
  // locally sent ones, so a mid-session refresh (fresh mount, empty
  // state) showed an empty thread even though messages/sync had already
  // durably persisted everything — confirmed live via
  // GET /astrologer/consultation/messages, which mirrors the
  // customer-side transcript endpoint. Only called once per mount
  // (guarded by joinedAgoraRef alongside joinAgora above), never on
  // every 4s poll tick.
  async function loadPriorMessages(id: number, token: string) {
    const response = await getAstrologerConsultationMessages(id, token);
    if (!response?.success) return;

    const items = response.data?.items ?? response.data?.data?.items ?? [];
    if (items.length === 0) return;

    setMessages((prev) => {
      const seen = new Set(prev.map((m) => m.clientMessageId));
      const loaded = items
        .filter((m: any) => !seen.has(m.client_message_id))
        .map((m: any) => ({
          clientMessageId: m.client_message_id,
          senderType: m.sender_type,
          body: m.body,
          sentAt: m.sent_at,
        }));
      return [...loaded, ...prev];
    });

    // Fallback for resuming without ever having witnessed the live RTM
    // event that carries the real `meta` payload (e.g. a refresh right
    // after accepting, before the message arrived) — the real synced
    // transcript still has the message body itself, just parseable
    // back into structured fields instead of reading them off a
    // dedicated field, since messages/sync has no such field.
    const autoDetailsItem = items.find((m: any) => (m.body || "").includes("My details:"));
    if (autoDetailsItem) {
      const parsed = parseAutoDetailsMessage(autoDetailsItem.body);
      if (Object.values(parsed).some(Boolean)) {
        setCustomerDetails((prev) => ({ ...parsed, ...prev }));
        cacheConsultationCustomerDetails(id, parsed);
      }
    }
  }

  async function joinAgora(agora: any, channelName: string, med?: string) {
    channelNameRef.current = channelName;
    myRtmAccountRef.current = agora.rtm_account;

    try {
      const rtm = await createAndLoginRtm(agora.rtm_account, agora.rtm_token);
      rtmClientRef.current = rtm;
      await subscribeRtmChannel(rtm, channelName);

      rtmUnsubscribeRef.current = onRtmMessage(rtm, (payload, publisher) => {
        if (publisher === agora.rtm_account) return;

        setMessages((prev) => [
          ...prev,
          {
            clientMessageId: payload.clientMessageId,
            senderType: "USER",
            body: payload.body,
            sentAt: payload.sentAt,
          },
        ]);

        // The one real, confirmed source of the customer's birth
        // details/gender/marital-status/topic — see the comment on
        // ConsulteeAutoDetails in app/lib/consultation.ts. `meta` only
        // rides along on the live RTM payload (messages/sync has no
        // field for it), so cache it immediately so it survives a
        // later refresh the same way the accept-time cache does.
        if (payload.meta && Object.values(payload.meta).some(Boolean)) {
          setCustomerDetails((prev) => ({ ...prev, ...payload.meta }));
          cacheConsultationCustomerDetails(consultationId, payload.meta);
        }
      });

      if (med === "AUDIO" && agora.rtc_token) {
        rtcSessionRef.current = await joinRtcAudio({
          channel: channelName,
          token: agora.rtc_token,
          uid: agora.rtc_uid,
          onRemoteAudio: (play) => play(),
        });
      }
    } catch (error) {
      console.error("Agora join failed:", error);
      toast.error("Unable to connect the live session.");
    }

    // Best-effort, non-fatal — see the identical comment on the
    // customer-side page for why this can't key off the astrologer's
    // own account id (there isn't one available here) and uses the
    // consultation id instead. Also surfaces a loading state — see the
    // customer page's identical comment for why (an 8-10s connect +
    // history-restore chain is normal, not broken).
    setLoadingAttachmentHistory(true);

    try {
      const token = tokenRef.current;
      if (!token) return;

      const mint = await mintAgoraChatToken("astrologer", consultationId, token);

      if (mint.success && mint.appId && mint.username && mint.peerUsername && mint.token) {
        const chatSession = await loginAgoraChat(
          mint.appId,
          mint.username,
          mint.peerUsername,
          mint.token
        );

        chatSessionRef.current = chatSession;

        chatUnsubscribeRef.current = onAgoraChatAttachment(chatSession, (payload) => {
          setMessages((prev) => [
            ...prev,
            {
              clientMessageId: payload.clientMessageId,
              senderType: "USER",
              body: "",
              sentAt: payload.sentAt,
              attachment: {
                kind: payload.kind,
                url: payload.url,
                thumbUrl: payload.thumbUrl,
                durationSeconds: payload.durationSeconds,
              },
            },
          ]);
        });

        // Restores image/voice messages sent before this page load —
        // see loadAgoraChatAttachmentHistory's own comment (in
        // app/lib/agora.ts) for why this is needed on top of
        // onAgoraChatAttachment above.
        try {
          const history = await loadAgoraChatAttachmentHistory(chatSession);

          if (history.length) {
            setMessages((prev) => {
              const seen = new Set(prev.map((m) => m.clientMessageId));

              const restored = history
                .filter((item) => !seen.has(item.clientMessageId))
                .map((item) => ({
                  clientMessageId: item.clientMessageId,
                  senderType: (item.from === chatSession.username ? "ASTROLOGER" : "USER") as
                    | "USER"
                    | "ASTROLOGER",
                  body: "",
                  sentAt: item.sentAt,
                  attachment: {
                    kind: item.kind,
                    url: item.url,
                    thumbUrl: item.thumbUrl,
                    durationSeconds: item.durationSeconds,
                  },
                }));

              return [...prev, ...restored].sort(
                (a, b) => new Date(a.sentAt).getTime() - new Date(b.sentAt).getTime()
              );
            });
          }
        } catch (error) {
          console.error("Agora Chat history load failed:", error);
        }
      }
    } catch (error) {
      console.error("Agora Chat join failed:", error);
    } finally {
      setLoadingAttachmentHistory(false);
    }
  }

  function teardownAgora() {
    rtmUnsubscribeRef.current?.();
    if (rtmClientRef.current) logoutRtm(rtmClientRef.current);
    leaveRtcAudio(rtcSessionRef.current);
    rtmClientRef.current = null;
    rtcSessionRef.current = null;

    chatUnsubscribeRef.current?.();
    logoutAgoraChat(chatSessionRef.current);
    chatSessionRef.current = null;

    recordingStreamRef.current?.getTracks().forEach((track) => track.stop());
    recordingStreamRef.current = null;
    if (recordingIntervalRef.current) clearInterval(recordingIntervalRef.current);
  }

  const flushMessages = async (isFinal = false, keepalive = false) => {
    if (pendingSyncRef.current.length === 0 && !isFinal) return;
    const batch = pendingSyncRef.current;
    pendingSyncRef.current = [];

    const token = tokenRef.current;
    if (!token) {
      pendingSyncRef.current = [...batch, ...pendingSyncRef.current];
      return;
    }

    await syncAstrologerConsultationMessages(consultationId, batch, isFinal, token, keepalive).catch(() => {
      pendingSyncRef.current = [...batch, ...pendingSyncRef.current];
    });
  };

  useEffect(() => {
    if (phase !== "ONGOING") return;
    const interval = setInterval(() => flushMessages(false), 30000);
    return () => clearInterval(interval);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [phase]);

  // Same gap as the customer page: a message sent just before a
  // refresh/close, before the 30s timer or the 25-message batch fired,
  // was only ever in memory and never reached the server — confirmed
  // live. `keepalive: true` lets the sync request finish sending after
  // the page is already gone.
  useEffect(() => {
    if (phase !== "ONGOING") return;

    const handler = () => {
      if (pendingSyncRef.current.length === 0) return;
      flushMessages(false, true);
    };

    window.addEventListener("pagehide", handler);
    window.addEventListener("beforeunload", handler);
    return () => {
      window.removeEventListener("pagehide", handler);
      window.removeEventListener("beforeunload", handler);
    };
  }, [phase]);

  // Shared by the composer (handleSend) and the "Link" attachment
  // option (handleSendLink) — same real RTM-publish + sync-queue
  // pipeline either way, since a shared link is just a normal message
  // whose body happens to be a URL (see isLikelyUrl/renderMessageBody
  // below); the messaging API itself is confirmed plain-text only.
  const sendMessage = async (text: string) => {
    if (!text || !myRtmAccountRef.current || !channelNameRef.current) return;

    const clientMessageId = `${myRtmAccountRef.current}:${Date.now()}:${sequenceRef.current++}`;
    const sentAt = new Date().toISOString().slice(0, 19).replace("T", " ");
    const payload = { clientMessageId, body: text, sentAt };

    setMessages((prev) => [
      ...prev,
      { clientMessageId, senderType: "ASTROLOGER", body: text, sentAt },
    ]);

    pendingSyncRef.current.push({
      client_message_id: clientMessageId,
      sender_type: "ASTROLOGER",
      message_type: "TEXT",
      body: text,
      sent_at: sentAt,
      sequence: sequenceRef.current,
    });

    try {
      await publishRtmMessage(rtmClientRef.current, channelNameRef.current, payload);
    } catch {
      toast.error("Message may not have been delivered.");
    }

    if (pendingSyncRef.current.length >= 25) flushMessages(false);
  };

  const handleSend = async () => {
    const text = composerValue.trim();
    if (!text) return;
    setComposerValue("");
    await sendMessage(text);
  };

  // Voice typing — dictates straight into the composer text box, same
  // as the customer-side page (see its identical comment).
  const dictationBaseRef = useRef("");

  const voiceTyping = useVoiceTyping((liveText) => {
    const base = dictationBaseRef.current;
    setComposerValue(base ? `${base} ${liveText}` : liveText);
  });

  const toggleVoiceTyping = () => {
    if (voiceTyping.isListening) {
      voiceTyping.stop();
      return;
    }

    if (!voiceTyping.isSupported) {
      toast.error("Voice typing isn't supported in this browser — try Chrome or Edge.");
      return;
    }

    dictationBaseRef.current = composerValue.trim();
    voiceTyping.start();
  };

  const handleSendLink = async () => {
    const raw = linkDraft.trim();
    if (!raw) return;

    const url = /^https?:\/\//i.test(raw) ? raw : `https://${raw}`;

    setLinkDraft("");
    setShowLinkInput(false);
    setShowAttachmentSheet(false);
    await sendMessage(url);
  };

  // "Photo" in the attachment sheet — a real Agora Chat image message,
  // same as the customer-side page. Own echo added to `messages`
  // locally since this doesn't go through the RTM text channel.
  const handlePickImage = () => {
    setShowAttachmentSheet(false);
    imageInputRef.current?.click();
  };

  const handleImageSelected = async (event: React.ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0];
    event.target.value = "";
    if (!file || !chatSessionRef.current) {
      if (!chatSessionRef.current) {
        toast.error("Attachments aren't available right now — try again in a moment.");
      }
      return;
    }

    try {
      setSendingImage(true);
      const sent = await sendAgoraChatImage(chatSessionRef.current, file);

      setMessages((prev) => [
        ...prev,
        {
          clientMessageId: sent.clientMessageId,
          senderType: "ASTROLOGER",
          body: "",
          sentAt: sent.sentAt,
          attachment: { kind: "image", url: sent.url, thumbUrl: sent.thumbUrl },
        },
      ]);
    } catch (error) {
      console.error("Send image error:", error);
      toast.error("Unable to send that photo — try again.");
    } finally {
      setSendingImage(false);
    }
  };

  // Voice recording — same tap-to-start/tap-to-stop flow as the
  // customer-side page.
  const startVoiceRecording = async () => {
    try {
      const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
      recordingStreamRef.current = stream;
      recordedChunksRef.current = [];

      const recorder = new MediaRecorder(stream);
      mediaRecorderRef.current = recorder;

      recorder.ondataavailable = (event) => {
        if (event.data.size > 0) recordedChunksRef.current.push(event.data);
      };

      recorder.start();
      setIsRecordingVoice(true);
      setRecordingSeconds(0);

      recordingIntervalRef.current = setInterval(() => {
        setRecordingSeconds((prev) => prev + 1);
      }, 1000);
    } catch (error) {
      console.error("Microphone access error:", error);
      toast.error("Couldn't access your microphone — check your browser permissions.");
    }
  };

  const stopRecordingStream = () => {
    if (recordingIntervalRef.current) clearInterval(recordingIntervalRef.current);
    recordingStreamRef.current?.getTracks().forEach((track) => track.stop());
    recordingStreamRef.current = null;
    setIsRecordingVoice(false);
  };

  const cancelVoiceRecording = () => {
    mediaRecorderRef.current?.stop();
    mediaRecorderRef.current = null;
    recordedChunksRef.current = [];
    stopRecordingStream();
  };

  const stopAndSendVoiceRecording = () => {
    const recorder = mediaRecorderRef.current;
    if (!recorder) return;

    const durationSeconds = recordingSeconds;

    recorder.onstop = async () => {
      stopRecordingStream();

      const blob = new Blob(recordedChunksRef.current, { type: recorder.mimeType || "audio/webm" });
      recordedChunksRef.current = [];

      if (!chatSessionRef.current || blob.size === 0) {
        if (!chatSessionRef.current) {
          toast.error("Attachments aren't available right now — try again in a moment.");
        }
        return;
      }

      const extension = (recorder.mimeType || "audio/webm").includes("mp4") ? "m4a" : "webm";
      const file = new File([blob], `voice-message.${extension}`, { type: blob.type });

      try {
        setSendingVoice(true);
        const sent = await sendAgoraChatAudio(chatSessionRef.current, file, durationSeconds);

        setMessages((prev) => [
          ...prev,
          {
            clientMessageId: sent.clientMessageId,
            senderType: "ASTROLOGER",
            body: "",
            sentAt: sent.sentAt,
            attachment: { kind: "audio", url: sent.url, durationSeconds },
          },
        ]);
      } catch (error) {
        console.error("Send voice message error:", error);
        toast.error("Unable to send that voice message — try again.");
      } finally {
        setSendingVoice(false);
      }
    };

    recorder.stop();
    mediaRecorderRef.current = null;
  };

  const handleEnd = async () => {
    const token = tokenRef.current;
    if (!token) return;

    try {
      setBusy(true);
      setShowEndConfirm(false);
      await flushMessages(true);

      const response = await endAstrologerConsultation(consultationId, token);
      if (!response.success) {
        toast.error(response.message || "Unable to end the consultation.");
      }

      teardownAgora();
      setPhase("ENDED");
    } finally {
      setBusy(false);
    }
  };

  const earnedSoFar =
    ratePerMinute != null ? Math.round((elapsedSeconds / 60) * ratePerMinute) : null;

  const formatTimer = (totalSeconds: number) => {
    const m = Math.floor(totalSeconds / 60).toString().padStart(2, "0");
    const s = Math.floor(totalSeconds % 60).toString().padStart(2, "0");
    return `${m}:${s}`;
  };

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

  const isChat = medium !== "AUDIO";
  const mediumLabel = isChat ? "Chat" : "Call";

  const insertQuickReply = (text: string) => {
    setComposerValue((prev) => (prev.trim() ? `${prev.trim()} ${text}` : text));
  };

  const quickReplies = QUICK_REPLIES_BY_LANGUAGE[replyLanguage] || QUICK_REPLIES_BY_LANGUAGE.eng;

  return (
    <div className="astrologer-dashboard-shell">
      <DashboardSidebar active="Orders" />

      <main className="astrologer-dashboard-main">
        <nav className="consult-breadcrumb">
          <span>Home</span> <span>›</span> <span>Orders</span> <span>›</span>
          <span className="active">{mediumLabel}</span>
        </nav>

        <h1 className="consult-page-title">{mediumLabel} Consultation</h1>
        <p className="consult-page-subtitle">
          {phase === "ONGOING"
            ? `You're live with ${userName || "your customer"} — keep it clear and helpful.`
            : "Real-time consultations with your customers."}
        </p>

        <div className="astrologer-panel card-bg-primary">
          {phase === "loading" && <p className="mb-0">Loading...</p>}

          {phase === "NONE" && <p className="mb-0">This consultation is no longer active.</p>}

          {phase === "ACCEPTED" && (
            <div className="text-center py-4 consult-connecting">
              <div className="consult-orbit mx-auto mb-3">
                <div className="consult-orbit-ring">
                  <span className="consult-orbit-dot dot-1" />
                  <span className="consult-orbit-dot dot-2" />
                  <span className="consult-orbit-dot dot-3" />
                </div>
                <div className="consult-avatar-ring">
                  <span>{initials(userName)}</span>
                </div>
                <div className="consult-orbit-badge">{isChat ? "💬" : "📞"}</div>
              </div>

              <h5 className="mb-1">Waiting for {userName || "the customer"} to join</h5>
              <p className="terms-text-color small mb-4">
                You accepted this {mediumLabel.toLowerCase()} — it'll start the moment they join.
              </p>

              <div className="consult-connecting-note mx-auto mb-4">
                Please wait — the {mediumLabel.toLowerCase()} will start automatically once{" "}
                {userName || "the customer"} joins. Held for {joinSecondsLeft}s.
              </div>

              <button
                type="button"
                className="btn btn-warning fw-bold px-5"
                onClick={() => setShowEndConfirm(true)}
                disabled={busy}
              >
                {busy ? "Cancelling..." : "Cancel"}
              </button>
            </div>
          )}

          {phase === "ENDED" && <p className="mb-0">Consultation ended.</p>}
        </div>

        {phase === "ONGOING" && (
          <div className="consult-layout">
            <div className="consult-main-col">
              {isChat && (
                <div className="astrologer-panel card-bg-primary consult-header-card">
                  <div className="d-flex align-items-center gap-2">
                    <div className="consult-avatar-sm consult-avatar-online">{initials(userName)}</div>
                    <div>
                      <h5 className="mb-0">{userName || "Customer"}</h5>
                      <span className="terms-text-color small">
                        {mediumLabel}
                        {ratePerMinute != null ? ` · ₹${ratePerMinute}/min` : ""}
                      </span>
                    </div>
                  </div>

                  <div className="d-flex align-items-center gap-3">
                    <div className="text-end">
                      <div className="consult-timer">{formatTimer(elapsedSeconds)}</div>
                      {earnedSoFar != null && (
                        <div className="terms-text-color small">₹{earnedSoFar} earned</div>
                      )}
                    </div>

                    <button
                      type="button"
                      className="btn btn-outline-warning fw-bold px-3"
                      onClick={() => setShowKundliModal(true)}
                    >
                      🔯 Kundli
                    </button>

                    <button
                      type="button"
                      className="btn btn-danger fw-bold px-4"
                      onClick={() => setShowEndConfirm(true)}
                      disabled={busy}
                    >
                      {busy ? "Ending..." : `End ${mediumLabel.toLowerCase()}`}
                    </button>
                  </div>
                </div>
              )}

              {isChat ? (
                <div className="astrologer-panel card-bg-primary consult-messages-panel">
                  {messages.length === 0 && !loadingAttachmentHistory && (
                    <p className="terms-text-color small mb-0 text-center">No messages yet.</p>
                  )}

                  {loadingAttachmentHistory && (
                    <p className="terms-text-color small mb-3 text-center">
                      Loading photo/voice history...
                    </p>
                  )}

                  {messages.map((m) => {
                    const mine = m.senderType === "ASTROLOGER";
                    return (
                      <div
                        key={m.clientMessageId}
                        className={`consult-msg-row ${mine ? "mine" : ""}`}
                      >
                        {!mine && (
                          <div className="consult-msg-avatar">{initials(userName)}</div>
                        )}
                        <div className={`consult-msg-bubble ${m.attachment ? "media" : ""}`}>
                          {m.attachment?.kind === "image" ? (
                            <a href={m.attachment.url} target="_blank" rel="noopener noreferrer">
                              <img
                                src={m.attachment.thumbUrl || m.attachment.url}
                                alt="Shared photo"
                                className="chat-message-image"
                              />
                            </a>
                          ) : m.attachment?.kind === "audio" ? (
                            <audio controls src={m.attachment.url} className="chat-message-audio" />
                          ) : isLikelyUrl(m.body) ? (
                            <a
                              href={m.body}
                              target="_blank"
                              rel="noopener noreferrer"
                              className="consult-msg-link"
                            >
                              🔗 {m.body}
                            </a>
                          ) : (
                            m.body
                          )}
                          <div className="consult-msg-time">
                            {(m.sentAt || "").slice(11, 16)}
                          </div>
                        </div>
                        {mine && (
                          <div className="consult-msg-avatar self">
                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
                              <path d="M12 12a5 5 0 1 0-5-5 5 5 0 0 0 5 5Zm0 2c-4.42 0-8 2.24-8 5v1h16v-1c0-2.76-3.58-5-8-5Z" />
                            </svg>
                          </div>
                        )}
                      </div>
                    );
                  })}
                </div>
              ) : (
                <div className="astrologer-panel card-bg-primary py-4 text-center consult-call-screen">
                  <span className="consult-call-pill mb-4">
                    <svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="currentColor">
                      <path d="M6.6 10.8c1.4 2.8 3.8 5.1 6.6 6.6l2.2-2.2c.3-.3.7-.4 1-.2 1.1.4 2.3.6 3.5.6.6 0 1 .4 1 1V20c0 .6-.4 1-1 1C10.6 21 3 13.4 3 4c0-.6.4-1 1-1h3.5c.6 0 1 .4 1 1 0 1.2.2 2.4.6 3.5.1.4 0 .8-.2 1L6.6 10.8Z" />
                    </svg>
                    Call connected
                  </span>

                  <div className="consult-avatar-ring mx-auto mb-3">
                    <span>{initials(userName)}</span>
                  </div>

                  <h5 className="mb-1">{userName || "Customer"}</h5>
                  <p className="terms-text-color small mb-4">
                    {mediumLabel}
                    {ratePerMinute != null ? ` · ₹${ratePerMinute}/min` : ""}
                  </p>

                  <div className="consult-timer consult-timer-lg mb-1">{formatTimer(elapsedSeconds)}</div>
                  {earnedSoFar != null && (
                    <p className="terms-text-color small mb-4">₹{earnedSoFar} earned so far</p>
                  )}

                  <div className="consult-call-actions">
                    <button
                      type="button"
                      className={`consult-call-action-btn ${muted ? "active" : ""}`}
                      onClick={() => {
                        const next = !muted;
                        setMuted(next);
                        if (rtcSessionRef.current) toggleMic(rtcSessionRef.current, next);
                      }}
                    >
                      <span className="consult-call-action-circle">
                        {muted ? "🔇" : "🎤"}
                      </span>
                      {muted ? "Unmute" : "Mute"}
                    </button>

                    <button
                      type="button"
                      className="consult-call-action-btn disabled"
                      disabled
                      title="Not available in the browser — audio always plays through your system's default output"
                    >
                      <span className="consult-call-action-circle">🔊</span>
                      Speaker
                    </button>

                    <button
                      type="button"
                      className="consult-call-action-btn disabled"
                      disabled
                      title="Hold isn't supported yet — end the call instead if you need to step away"
                    >
                      <span className="consult-call-action-circle">⏸️</span>
                      Hold
                    </button>

                    <button
                      type="button"
                      className="consult-call-action-btn disabled"
                      disabled
                      title="Remedies isn't built yet"
                    >
                      <span className="consult-call-action-circle">📦</span>
                      Remedies
                    </button>
                  </div>

                  <button
                    type="button"
                    className="btn btn-outline-warning fw-bold w-100 mb-3"
                    onClick={() => setShowKundliModal(true)}
                  >
                    🔯 Kundli
                  </button>

                  <button
                    type="button"
                    className="consult-end-call-btn"
                    onClick={() => setShowEndConfirm(true)}
                    disabled={busy}
                  >
                    <span>⊗</span> {busy ? "Ending..." : "End call"}
                  </button>
                </div>
              )}

              {isChat && (
                <>
                  <div className="consult-quick-replies">
                    {quickReplies.map((text) => (
                      <button
                        key={text}
                        type="button"
                        className="consult-quick-reply-chip"
                        onClick={() => insertQuickReply(text)}
                      >
                        {text}
                      </button>
                    ))}
                  </div>

                  <div className="d-flex gap-2">
                    <button
                      type="button"
                      className="consult-attach-btn"
                      onClick={() => setShowAttachmentSheet(true)}
                      aria-label="Attach"
                      disabled={isRecordingVoice}
                    >
                      +
                    </button>
                    <button
                      type="button"
                      className="consult-attach-btn"
                      onClick={() => setShowUserDetailModal(true)}
                      aria-label="User detail"
                      title="User detail"
                      disabled={isRecordingVoice}
                    >
                      👤
                    </button>
                    <button
                      type="button"
                      className="consult-attach-btn"
                      onClick={() => setShowLanguageModal(true)}
                      aria-label="Choose language"
                      title="Choose language"
                      disabled={isRecordingVoice}
                    >
                      🌐
                    </button>
                    <button
                      type="button"
                      className="consult-attach-btn"
                      onClick={() => setShowPreviousOrdersModal(true)}
                      aria-label="Previous orders"
                      title="Previous orders"
                      disabled={isRecordingVoice}
                    >
                      🧾
                    </button>

                    <input
                      ref={imageInputRef}
                      type="file"
                      accept="image/*"
                      style={{ display: "none" }}
                      onChange={handleImageSelected}
                    />

                    {isRecordingVoice ? (
                      <div className="chat-recording-bar">
                        <span className="chat-recording-dot" />
                        <span>Recording... {formatTimer(recordingSeconds)}</span>
                        <button
                          type="button"
                          className="btn btn-outline-light btn-sm"
                          onClick={cancelVoiceRecording}
                        >
                          Cancel
                        </button>
                        <button
                          type="button"
                          className="btn btn-warning btn-sm fw-bold"
                          onClick={stopAndSendVoiceRecording}
                        >
                          Send
                        </button>
                      </div>
                    ) : (
                      <>
                        <input
                          className="form-control form-control-lg"
                          placeholder={voiceTyping.isListening ? "Listening..." : "Type a reply..."}
                          value={composerValue}
                          onChange={(e) => setComposerValue(e.target.value)}
                          onKeyDown={(e) => e.key === "Enter" && handleSend()}
                        />
                        <button
                          type="button"
                          className={`consult-attach-btn ${voiceTyping.isListening ? "active" : ""}`}
                          onClick={toggleVoiceTyping}
                          aria-label="Voice typing"
                          title={
                            voiceTyping.isSupported
                              ? "Voice typing — speak to type"
                              : "Voice typing isn't supported in this browser"
                          }
                        >
                          🎙️
                        </button>
                        <button type="button" className="btn btn-warning fw-bold px-4" onClick={handleSend}>
                          Send
                        </button>
                        <button
                          type="button"
                          className="consult-attach-btn"
                          onClick={startVoiceRecording}
                          disabled={sendingImage || sendingVoice}
                          aria-label="Record a voice message"
                          title="Record a voice message"
                        >
                          🎤
                        </button>
                      </>
                    )}
                  </div>
                </>
              )}
            </div>

            {showAttachmentSheet && (
              <div
                className="astrologer-modal-backdrop"
                onClick={() => {
                  setShowAttachmentSheet(false);
                  setShowLinkInput(false);
                }}
              >
                <div className="astrologer-modal attachment-modal" onClick={(e) => e.stopPropagation()}>
                  <h5 className="mb-2">{showLinkInput ? "Share a link" : "Send something"}</h5>
                  <p className="terms-text-color small mb-4">
                    {showLinkInput
                      ? "Sent as a real message — the customer can open it directly."
                      : "Attachments stay in this thread's history."}
                  </p>

                  {showLinkInput ? (
                    <form
                      onSubmit={(event) => {
                        event.preventDefault();
                        handleSendLink();
                      }}
                    >
                      <input
                        type="url"
                        autoFocus
                        className="form-control form-control-lg mb-3"
                        placeholder="https://example.com"
                        value={linkDraft}
                        onChange={(event) => setLinkDraft(event.target.value)}
                      />
                      <div className="d-flex gap-2">
                        <button
                          type="button"
                          className="btn btn-outline-light flex-fill"
                          onClick={() => setShowLinkInput(false)}
                        >
                          Back
                        </button>
                        <button
                          type="submit"
                          className="btn btn-warning fw-bold flex-fill"
                          disabled={!linkDraft.trim()}
                        >
                          Send
                        </button>
                      </div>
                    </form>
                  ) : (
                    <div className="attachment-grid">
                      <button
                        type="button"
                        className="attachment-option"
                        onClick={handlePickImage}
                      >
                        <span className="attachment-icon">▧</span>
                        <strong>Photo</strong>
                        <small>Camera or gallery</small>
                      </button>

                      <button
                        type="button"
                        className="attachment-option"
                        onClick={() => {
                          setShowAttachmentSheet(false);
                          toast("Remedies selected.");
                        }}
                      >
                        <span className="attachment-icon">◇</span>
                        <strong>Remedies</strong>
                        <small>Send a remedy</small>
                      </button>

                      <button
                        type="button"
                        className="attachment-option"
                        onClick={() => setShowLinkInput(true)}
                      >
                        <span className="attachment-icon">🔗</span>
                        <strong>Link</strong>
                        <small>Share a URL</small>
                      </button>
                    </div>
                  )}
                </div>
              </div>
            )}

            {showUserDetailModal && (
              <div className="astrologer-modal-backdrop" onClick={() => setShowUserDetailModal(false)}>
                <div className="astrologer-modal" onClick={(e) => e.stopPropagation()}>
                  <div className="d-flex justify-content-between align-items-start mb-2">
                    <h5 className="mb-0">User Detail</h5>
                    <button
                      type="button"
                      className="btn-close btn-close-white"
                      aria-label="Close"
                      onClick={() => setShowUserDetailModal(false)}
                    />
                  </div>
                  <p className="terms-text-color small mb-3">
                    Details {userName || "the customer"} shared for this session.
                  </p>

                  <div className="consult-detail-row">
                    <div className="consult-detail-icon">👤</div>
                    <div>
                      <div className="terms-text-color small">Name</div>
                      <div className="fw-semibold">{customerDetails.name || userName || "—"}</div>
                    </div>
                  </div>

                  <div className="consult-detail-row">
                    <div className="consult-detail-icon">🚻</div>
                    <div>
                      <div className="terms-text-color small">Gender</div>
                      <div className="fw-semibold">{customerDetails.gender || "—"}</div>
                    </div>
                  </div>

                  <div className="consult-detail-row">
                    <div className="consult-detail-icon">🎂</div>
                    <div>
                      <div className="terms-text-color small">Date of birth</div>
                      <div className="fw-semibold">{customerDetails.birthDate || "—"}</div>
                    </div>
                  </div>

                  <div className="consult-detail-row">
                    <div className="consult-detail-icon">🕐</div>
                    <div>
                      <div className="terms-text-color small">Time of birth</div>
                      <div className="fw-semibold">{customerDetails.birthTime || "—"}</div>
                    </div>
                  </div>

                  <div className="consult-detail-row">
                    <div className="consult-detail-icon">📍</div>
                    <div>
                      <div className="terms-text-color small">Place of birth</div>
                      <div className="fw-semibold">{customerDetails.birthPlace || "—"}</div>
                    </div>
                  </div>

                  <button
                    type="button"
                    className="btn btn-link p-0 mt-3 text-decoration-none"
                    disabled
                    title="Notes aren't available yet"
                  >
                    📝 Add/View Notes
                  </button>
                </div>
              </div>
            )}

            {showLanguageModal && (
              <div className="astrologer-modal-backdrop" onClick={() => setShowLanguageModal(false)}>
                <div className="astrologer-modal" onClick={(e) => e.stopPropagation()}>
                  <div className="d-flex justify-content-between align-items-start mb-3">
                    <h5 className="mb-0">Choose language</h5>
                    <button
                      type="button"
                      className="btn-close btn-close-white"
                      aria-label="Close"
                      onClick={() => setShowLanguageModal(false)}
                    />
                  </div>

                  <p className="terms-text-color small mb-3">
                    Changes which quick-reply phrases show above the composer — it
                    doesn&apos;t translate messages already sent or received.
                  </p>

                  <div className="consult-language-grid">
                    {LANGUAGES.map((lang) => (
                      <button
                        key={lang.id}
                        type="button"
                        className={`consult-language-option ${replyLanguage === lang.id ? "active" : ""}`}
                        onClick={() => setReplyLanguage(lang.id)}
                      >
                        <strong>{lang.short}</strong>
                        <span>{lang.native}</span>
                      </button>
                    ))}
                  </div>

                  <button
                    type="button"
                    className="btn btn-warning fw-bold w-100 mt-3"
                    onClick={() => setShowLanguageModal(false)}
                  >
                    Apply
                  </button>
                </div>
              </div>
            )}

            {showPreviousOrdersModal && (
              <div className="astrologer-modal-backdrop" onClick={() => setShowPreviousOrdersModal(false)}>
                <div className="astrologer-modal" onClick={(e) => e.stopPropagation()}>
                  <div className="d-flex justify-content-between align-items-start mb-2">
                    <h5 className="mb-0">Previous Orders</h5>
                    <button
                      type="button"
                      className="btn-close btn-close-white"
                      aria-label="Close"
                      onClick={() => setShowPreviousOrdersModal(false)}
                    />
                  </div>

                  <p className="terms-text-color small mb-0">
                    Order history scoped to one customer isn&apos;t available yet — the
                    backend doesn&apos;t currently expose a per-customer order list, only an
                    astrologer-wide settlement history across every customer.
                  </p>
                </div>
              </div>
            )}

            <div className="consult-side-col">
              <div className="astrologer-panel card-bg-primary">
                <h6 className="fw-bold mb-3">Consultation Details</h6>

                <div className="consult-detail-row">
                  <div className="consult-detail-icon">{isChat ? "💬" : "📞"}</div>
                  <div>
                    <div className="terms-text-color small">Type</div>
                    <div className="fw-semibold">{mediumLabel} Consultation</div>
                  </div>
                </div>

                {ratePerMinute != null && (
                  <div className="consult-detail-row">
                    <div className="consult-detail-icon">🏷️</div>
                    <div>
                      <div className="terms-text-color small">Rate</div>
                      <div className="fw-semibold">₹{ratePerMinute}/min</div>
                    </div>
                  </div>
                )}

                <div className="consult-detail-row">
                  <div className="consult-detail-icon">⏱️</div>
                  <div>
                    <div className="terms-text-color small">Duration</div>
                    <div className="fw-semibold">{formatTimer(elapsedSeconds)}</div>
                  </div>
                </div>

                {earnedSoFar != null && (
                  <div className="consult-detail-row">
                    <div className="consult-detail-icon">✨</div>
                    <div>
                      <div className="terms-text-color small">Earned Amount</div>
                      <div className="fw-semibold">₹{earnedSoFar}</div>
                    </div>
                  </div>
                )}
              </div>

              <div className="astrologer-panel card-bg-primary">
                <div className="d-flex align-items-center gap-3">
                  <div className="consult-avatar-sm">{initials(userName)}</div>
                  <div>
                    <div className="fw-bold">{userName || "Customer"}</div>
                    <div className="small" style={{ color: "#22C55E" }}>
                      ● Online
                    </div>
                  </div>
                </div>
              </div>

              
            </div>
          </div>
        )}
      </main>

      {showEndConfirm && (
        <div className="astrologer-modal-backdrop" onClick={() => setShowEndConfirm(false)}>
          <div className="astrologer-modal" onClick={(e) => e.stopPropagation()}>
            <h5 className="mb-2">
              {phase === "ACCEPTED" ? "Cancel this request?" : medium === "AUDIO" ? "End call?" : "End chat?"}
            </h5>
            <p className="terms-text-color small mb-4">
              {phase === "ACCEPTED"
                ? `Are you sure you want to cancel? ${userName || "The customer"} won't be charged — nothing has started yet.`
                : `Are you sure you want to end this ${medium === "AUDIO" ? "call" : "chat"}? The session will be closed for both sides.`}
            </p>
            <div className="d-flex gap-2">
              <button
                type="button"
                className="btn btn-outline-light flex-fill"
                onClick={() => setShowEndConfirm(false)}
              >
                Back
              </button>
              <button
                type="button"
                className="btn btn-warning fw-bold flex-fill"
                onClick={handleEnd}
                disabled={busy}
              >
                {busy
                  ? "Cancelling..."
                  : phase === "ACCEPTED"
                    ? "Cancel request"
                    : medium === "AUDIO"
                      ? "End call"
                      : "End chat"}
              </button>
            </div>
          </div>
        </div>
      )}

      <KundliModal
        show={showKundliModal}
        onClose={() => setShowKundliModal(false)}
        birthDetails={{
          name: customerDetails.name || userName,
          gender: customerDetails.gender,
          birthDate: customerDetails.birthDate,
          birthTime: customerDetails.birthTime,
          birthPlace: customerDetails.birthPlace,
        }}
      />
    </div>
  );
}
