"use client";

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

import Header from "../../components/layout/Header";
import Footer from "../../components/layout/Footer";
import "./consultation.css";
import Link from "@/app/components/LocaleLink";
import { useLocaleRouter } from "@/app/lib/useLocaleRouter";
import { useVoiceTyping } from "@/app/lib/useVoiceTyping";
import {
  getActiveConsultation,
  getConsultationStatus,
  startChatConsultation,
  cancelConsultation,
  tickConsultation,
  continueConsultationPaid,
  extendConsultation,
  endConsultation,
  syncConsultationMessages,
  getConsultationMessages,
  ConsultationMessage,
  buildAutoDetailsMessage,
  getCachedSubmittedConsulteeDetails,
  isAutoDetailsMessage,
} from "@/app/lib/consultation";
import {
  createAndLoginRtm,
  subscribeRtmChannel,
  onRtmMessage,
  publishRtmMessage,
  logoutRtm,
  joinRtcAudio,
  leaveRtcAudio,
  toggleMic,
  RtcAudioSession,
  RtmMessagePayload,
  mintAgoraChatToken,
  loginAgoraChat,
  logoutAgoraChat,
  onAgoraChatAttachment,
  loadAgoraChatAttachmentHistory,
  sendAgoraChatImage,
  sendAgoraChatAudio,
  ChatSession,
} from "@/app/lib/agora";

// Confirmed live: poll /status every 3s while REQUESTED, and tick every
// tick_interval_seconds (30s) once ONGOING — never at tick_timeout_seconds,
// which is a separate field with a 4x ratio (240s chat / 120s call).
const STATUS_POLL_MS = 3000;
const TICK_POLL_MS = 30000;

type Phase =
  | "loading"
  | "REQUESTED"
  | "ACCEPTED"
  | "ONGOING"
  | "MISSED"
  | "REJECTED"
  | "CANCELLED"
  | "COMPLETED"
  | "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 — a
  // durable, hosted attachment isn't something RTM's plain pub/sub
  // was ever meant to carry.
  attachment?: {
    kind: "image" | "audio";
    url: string;
    thumbUrl?: string;
    durationSeconds?: number;
  };
}

interface Receipt {
  duration?: string;
  duration_seconds?: number;
  billed_minutes?: number;
  rate_per_minute?: number;
  deducted_amount?: number;
  wallet_balance?: number;
  ended_at?: string;
}

function formatSeconds(totalSeconds = 0): string {
  const minutes = Math.floor(Math.max(0, totalSeconds) / 60);
  const seconds = Math.max(0, totalSeconds) % 60;
  return `${minutes}:${seconds.toString().padStart(2, "0")}`;
}

function formatDatePart(isoLocal?: string | null): string {
  if (!isoLocal) return "-";
  const date = new Date(isoLocal.replace(" ", "T"));
  if (Number.isNaN(date.getTime())) return "-";
  return date.toLocaleDateString("en-IN", { day: "2-digit", month: "short", year: "numeric" });
}

function formatTimePart(isoLocal?: string | null): string {
  if (!isoLocal) return "-";
  const date = new Date(isoLocal.replace(" ", "T"));
  if (Number.isNaN(date.getTime())) return "-";
  return date.toLocaleTimeString("en-IN", { hour: "2-digit", minute: "2-digit" });
}

// 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;
  // Server timestamps are "YYYY-MM-DD HH:mm:ss", local, no offset —
  // treat the device clock as close enough for a UI countdown; the
  // server's own deadline enforcement is what actually matters.
  const target = new Date(isoLocal.replace(" ", "T")).getTime();
  return Math.max(0, Math.round((target - Date.now()) / 1000));
}

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

  const [phase, setPhase] = useState<Phase>("loading");
  const [astrologerName, setAstrologerName] = useState("");
  const [astrologerImage, setAstrologerImage] = useState("");
  const [astrologerId, setAstrologerId] = useState<number | null>(null);
  const [medium, setMedium] = useState<"CHAT" | "AUDIO" | "VIDEO" | undefined>();
  const [ringSecondsLeft, setRingSecondsLeft] = useState(0);
  const [joinSecondsLeft, setJoinSecondsLeft] = useState(0);
  const [remainingSeconds, setRemainingSeconds] = useState(0);
  const [currentCharge, setCurrentCharge] = useState(0);
  const [walletBalance, setWalletBalance] = useState(0);
  // Confirmed live: current_charge from tick is the cumulative amount
  // charged so far, not a per-minute rate — the header used to render
  // it as "₹{currentCharge}/min", which is a mislabeling bug fixed
  // alongside this redesign, not a new field added for its own sake.
  const [ratePerMinute, setRatePerMinute] = useState<number | null>(null);
  const [startedAt, setStartedAt] = useState<string | null>(null);
  const [warningType, setWarningType] = useState<string | null>(null);
  const [messages, setMessages] = useState<ChatBubble[]>([]);
  const [composerValue, setComposerValue] = useState("");
  const [muted, setMuted] = useState(false);
  const [receipt, setReceipt] = useState<Receipt | null>(null);
  const [busy, setBusy] = useState(false);

  /* ==========================================
    END CHAT / REVIEW
  ========================================== */

  const [showEndConfirm, setShowEndConfirm] = useState(false);
  const [showReview, setShowReview] = useState(false);

  const [reviewRating, setReviewRating] = useState(0);
  const [reviewText, setReviewText] = useState("");

  const [selectedReviewTags, setSelectedReviewTags] = useState<string[]>([]);

  const [reviewSubmitting, setReviewSubmitting] = useState(false);
  const [reviewSubmitted, setReviewSubmitted] = useState(false);

  const [reviewError, setReviewError] = useState("");

  /* ==========================================
    ATTACHMENT
  ========================================== */

  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 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 warningLatchRef = useRef<string | null>(null);
  const endedRef = 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);

  // ---- token/init ----------------------------------------------------

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

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

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

    let cancelled = false;

    getActiveConsultation().then((response) => {
      if (cancelled) return;

      if (!response.success || !response.data?.has_active) {
        setPhase("NONE");
        return;
      }

      const data = response.data;

      setAstrologerName(data.astrologer_name || "");
      setAstrologerImage(data.astrologer_image || "");

      const rate = data.rate_per_minute ?? data.ratePerMinute;
      if (typeof rate === "number") setRatePerMinute(rate);
      if (data.started_at) setStartedAt(data.started_at);

      setAstrologerId(
        Number(
          data.astrologer_id ??
          data.astrologerId ??
          data.astrologer?.id ??
          data.astrologer?.ID ??
          0
        ) || null
      );

      setMedium(data.medium);
      setWalletBalance(data.wallet_balance ?? 0);

      if (data.status === "ONGOING" && data.agora) {
        setPhase("ONGOING");
        setRemainingSeconds(data.remaining_seconds ?? 0);
        joinAgora(data.agora, data.channel_name, data.medium);
        loadPriorMessages(consultationId);
      } else if (data.status === "ACCEPTED") {
        setPhase("ACCEPTED");
        // Prefer the server's own pre-computed remaining time — confirmed
        // live that computing this from ring_expires_at/join_expires_at
        // via the device clock drifts badly (device/server clocks
        // aren't in sync), exactly what the handbook warns against.
        setJoinSecondsLeft(data.seconds_to_join_end ?? secondsUntil(data.join_expires_at));
      } else if (data.status === "REQUESTED") {
        setPhase("REQUESTED");
        setRingSecondsLeft(data.seconds_to_expiry ?? secondsUntil(data.ring_expires_at));
      } else {
        setPhase("NONE");
      }
    });

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

  // ---- REQUESTED: poll /status every 3s -------------------------------

  useEffect(() => {
    if (phase !== "REQUESTED") return;

    let cancelled = false;

    const poll = async () => {
      const response = await getConsultationStatus(consultationId);
      if (cancelled || !response.success) return;

      const data = response.data;
      setRingSecondsLeft(data.seconds_to_expiry ?? 0);

      if (data.status === "ACCEPTED") {
        setAstrologerName(data.astrologer_name || astrologerName);
        setJoinSecondsLeft(data.seconds_to_join_end ?? secondsUntil(data.join_expires_at));
        setPhase("ACCEPTED");
      } else if (data.status === "MISSED" || data.status === "REJECTED") {
        setPhase(data.status);
      } else if (data.status === "COMPLETED") {
        setPhase("COMPLETED");
      }
    };

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

  // ---- local ring/join countdowns -------------------------------------

  useEffect(() => {
    if (phase !== "REQUESTED" && phase !== "ACCEPTED") return;

    const interval = setInterval(() => {
      if (phase === "REQUESTED") setRingSecondsLeft((s) => Math.max(0, s - 1));
      if (phase === "ACCEPTED") setJoinSecondsLeft((s) => Math.max(0, s - 1));
    }, 1000);

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

  // ---- local 1-second call timer ----------------------------------------
  // The real value only refreshes every TICK_POLL_MS (30s) from the server;
  // this local ticker fills the gap so the on-screen timer counts smoothly
  // instead of jumping in 30-second steps. The next server tick's
  // setRemainingSeconds() above re-syncs it to the authoritative value.

  useEffect(() => {
    if (phase !== "ONGOING") return;

    const interval = setInterval(() => {
      setRemainingSeconds((s) => Math.max(0, s - 1));
    }, 1000);

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

  // ---- resume: reload the durable transcript on refresh ----------------
  // `messages` only ever grew from incoming RTM events + locally sent
  // ones, so a mid-session refresh (new mount, empty state) showed an
  // empty thread even though the backend already has everything
  // messages/sync flushed — confirmed live, GET /user/consultation/messages
  // returns the same transcript history's detail page already reads.
  // Only called when resuming an already-ONGOING session, never on a
  // fresh start-chat (nothing to reload yet).

  async function loadPriorMessages(id: number) {
    const response = await getConsultationMessages(id, 1, 200);
    if (!response.success) return;

    const items = response.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) && !isAutoDetailsMessage(m.body))
        .map((m: any) => ({
          clientMessageId: m.client_message_id,
          senderType: m.sender_type,
          body: m.body,
          sentAt: m.sent_at,
        }));
      return [...loaded, ...prev];
    });
  }

  // ---- Agora join/teardown --------------------------------------------

  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; // our own echo

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

      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 — check your connection and try again.");
    }

    // Best-effort, non-fatal: image/voice attachments ride a separate
    // Agora Chat connection. If this fails, text still works fine
    // over RTM above — attachments just silently aren't available
    // this session rather than blocking the whole consultation.
    // Confirmed live this connect + history-restore chain can easily
    // take 8-10s (Agora's own DNS lookup step alone took ~4s in
    // testing) — surfacing a loading state so a slow network doesn't
    // read as "attachments are just gone" during that window.
    setLoadingAttachmentHistory(true);

    try {
      const token = localStorage.getItem("token");
      if (!token) return;

      const mint = await mintAgoraChatToken("user", 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: "ASTROLOGER",
              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 for why
        // this is needed on top of onAgoraChatAttachment above (that
        // only covers messages received *after* this connects).
        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 ? "USER" : "ASTROLOGER") 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);
  }

  // ---- ONGOING: tick every 30s -----------------------------------------

  useEffect(() => {
    if (phase !== "ONGOING") return;

    let cancelled = false;

    const runTick = async () => {
      const response = await tickConsultation(consultationId);
      if (cancelled || !response.success) return;

      const data = response.data;
      setRemainingSeconds(data.remaining_seconds ?? 0);
      setCurrentCharge(data.current_charge ?? 0);
      setWalletBalance(data.wallet_balance ?? 0);

      if (data.warning && warningLatchRef.current !== data.warning_type) {
        warningLatchRef.current = data.warning_type;
        setWarningType(data.warning_type);
      }

      if (data.should_disconnect && !endedRef.current) {
        endedRef.current = true;

        const reason =
          data.disconnect_reason === "wallet balance exhausted" ||
          data.disconnect_reason === "free chat minutes used"
            ? "INSUFFICIENT_BALANCE"
            : "USER_ENDED";

        await finishSession(reason, data.disconnect_reason === "session already ended");
      }
    };

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

  // ---- actions -----------------------------------------------------------

  const handleStartChat = async () => {
    try {
      setBusy(true);
      const response = await startChatConsultation(consultationId);

      if (!response.success) {
        toast.error(response.message || "Unable to start — the request may have expired.");
        return;
      }

      const data = response.data;
      setMedium(data.medium);
      setRemainingSeconds(data.max_billable_seconds ?? 0);

      const rate = data.rate_per_minute ?? data.ratePerMinute;
      if (typeof rate === "number") setRatePerMinute(rate);
      if (data.started_at) setStartedAt(data.started_at);

      setPhase("ONGOING");
      await joinAgora(data.agora, data.channel_name, data.medium);

      // Real message (sender_type "USER", the customer's own real
      // role), sent once right as chat genuinely starts — this is the
      // only place the astrologer's page has ever been confirmed to
      // actually receive the customer's birth details/gender/marital
      // status/topic (see the comment on ConsulteeAutoDetails).
      if (data.medium !== "AUDIO") {
        const submitted = getCachedSubmittedConsulteeDetails(consultationId);
        const astrologerFirstName = (astrologerName || "").trim().split(/\s+/)[0] || "";
        const autoText = buildAutoDetailsMessage(submitted, astrologerFirstName);
        if (autoText) {
          await sendMessage(autoText, submitted, true);
          // Force it into the durable transcript immediately rather
          // than waiting for the normal 30s/25-message batching —
          // confirmed live this was the real reason the astrologer's
          // Kundli button still showed "not enough details": the
          // astrologer's own loadPriorMessages() (its only fallback
          // once the live RTM delivery is missed — a real race, since
          // it only polls /active every 4s while this send can happen
          // faster) found nothing yet because the message hadn't been
          // synced to the backend at all.
          await flushMessages(false);
        }
      }
    } finally {
      setBusy(false);
    }
  };

  const handleCancel = async () => {
    setBusy(true);
    const response = await cancelConsultation(consultationId);
    setBusy(false);

    if (response.success) {
      setPhase("CANCELLED");
    } else {
      toast.error(response.message || "Unable to cancel.");
    }
  };

  const handleContinue = async (paid: boolean) => {
    const response = paid
      ? await continueConsultationPaid(consultationId)
      : await extendConsultation(consultationId);

    if (!response.success) {
      toast.error(response.message || "Unable to continue.");
      return;
    }

    warningLatchRef.current = null;
    setWarningType(null);
    setRemainingSeconds(response.data?.remaining_seconds ?? remainingSeconds);
  };

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

    const batch = pendingSyncRef.current;
    pendingSyncRef.current = [];

    await syncConsultationMessages(consultationId, batch, isFinal, keepalive).catch(() => {
      // Best-effort — retry on the next flush cycle rather than blocking.
      pendingSyncRef.current = [...batch, ...pendingSyncRef.current];
    });
  };

  // Flush periodically while ONGOING (every ~25 messages or 30s — a web
  // tab has no disk-persisted buffer to sweep on cold start, so this is
  // the honest web equivalent of the handbook's mobile guidance).
  useEffect(() => {
    if (phase !== "ONGOING") return;
    const interval = setInterval(() => flushMessages(false), 30000);
    return () => clearInterval(interval);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [phase]);

  // Flush whatever's still batched the moment the tab actually goes
  // away (refresh, close, navigate off-site) — confirmed live this was
  // the real remaining gap after loadPriorMessages() above: a message
  // sent seconds before a refresh, before the 30s timer or the
  // 25-message batch size ever fired, was still only in memory and
  // genuinely never reached the server, so reloading the (now correct)
  // durable transcript still didn't show it. `pagehide` fires more
  // reliably than `beforeunload` across browsers/mobile Safari; a
  // `keepalive: true` fetch is what lets the request actually finish
  // sending after the page is gone (sendBeacon can't carry the
  // Authorization header this proxy needs).
  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 auto-details message
  // sent right after chat starts — same real RTM-publish + sync-queue
  // pipeline either way, since the auto message is a genuine "USER"
  // sender_type message, not a local-only display trick. `meta` rides
  // along in the live RTM payload only (messages/sync has no field for
  // it) so the astrologer's page can read it directly instead of
  // parsing it back out of the text — see parseAutoDetailsMessage()
  // for the fallback that covers a history reload instead.
  //
  // `hideFromSender` is set for the auto-details message specifically
  // — per explicit request, the customer's own chat window should
  // never show it (it's meant for the astrologer to read), even though
  // it's a real message under the customer's own sender_type. It still
  // gets published/queued for sync exactly like any other message;
  // only the local echo into this page's own `messages` state is
  // skipped. isAutoDetailsMessage() filters the equivalent case out of
  // loadPriorMessages() above, so a refresh doesn't bring it back.
  const sendMessage = async (
    text: string,
    meta?: Record<string, any>,
    hideFromSender = false,
  ) => {
    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: RtmMessagePayload = { clientMessageId, body: text, sentAt };
    if (meta) payload.meta = meta;

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

    pendingSyncRef.current.push({
      client_message_id: clientMessageId,
      sender_type: "USER",
      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 (not
  // sent until the user hits Send), separate from the "record a voice
  // message" mic above which sends a real audio file as a message.
  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();
  };

  // "Link" in the attachment sheet — the real messaging API is plain
  // text (confirmed live: no attachment/media field anywhere in
  // messages/sync), so a shared link is just a normal message whose
  // body happens to be a URL; renderMessageBody() below is what turns
  // it into a clickable chip instead of plain text.
  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,
  // the SDK handles the upload to Agora's own hosted storage itself
  // (confirmed live: passing a File is enough, no separate upload
  // step needed). Own echo added to `messages` locally since this
  // doesn't go through the RTM text channel the rest of the UI reads
  // from.
  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: "USER",
          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 — tap the mic to start, tap again to stop and
  // send. Recorded as whatever MIME type the browser's MediaRecorder
  // actually supports (varies by browser), sent as a real Agora Chat
  // audio message the same way an image is.
  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: "USER",
            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;
  };

  async function finishSession(reason: "USER_ENDED" | "INSUFFICIENT_BALANCE", alreadyEnded: boolean) {
    await flushMessages(true);

    let data = null;

    if (!alreadyEnded) {
      const response = await endConsultation(consultationId, reason);
      if (response.success) data = response.data;
      else toast.error(response.message || "Unable to end the consultation.");
    }

    teardownAgora();
    setReceipt(data);
    setPhase("COMPLETED");
  }

  const handleEnd = async () => {
    if (endedRef.current) return;

    setShowEndConfirm(false);

    endedRef.current = true;
    setBusy(true);

    try {
      await finishSession(
        "USER_ENDED",
        false
      );

      /*
      * Show review after consultation ends.
      */
      setTimeout(() => {
        setShowReview(true);
      }, 300);
    } finally {
      setBusy(false);
    }
  };
  /* =====================================================
    SUBMIT USER REVIEW
  ===================================================== */

  const submitUserReview = async () => {
    if (!astrologerId) {
      setReviewError(
        "Astrologer information is missing."
      );
      return;
    }

    if (reviewRating < 1) {
      setReviewError(
        "Please select a rating."
      );
      return;
    }

    if (!reviewText.trim()) {
      setReviewError(
        "Please write your review."
      );
      return;
    }

    try {
      setReviewSubmitting(true);
      setReviewError("");

      const token =
        localStorage.getItem("token") ||
        localStorage.getItem("access_token") ||
        localStorage.getItem("user_token") ||
        "";

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

      /*
      * IMPORTANT:
      * Call our Next.js API route,
      * NOT the external API directly.
      */
      const response = await fetch(
        "/api/user/review/add",
        {
          method: "POST",

          headers: {
            "Content-Type": "application/json",
            Authorization: `Bearer ${token}`,
          },

          body: JSON.stringify({
            astrologerId: Number(astrologerId),

            rating: Number(reviewRating),

            review: reviewText.trim(),

            isPublic: true,
          }),
        }
      );

      const data = await response.json();

      console.log(
        "Review response:",
        data
      );

      if (!response.ok) {
        throw new Error(
          data?.message ||
            "Unable to submit review."
        );
      }

      /*
      * API success
      */
      setReviewSubmitted(true);

      toast.success(
        "Thank you! Your review has been submitted."
      );

    } catch (error: any) {
      console.error(
        "Submit review error:",
        error
      );

      setReviewError(
        error?.message ||
          "Unable to submit your review."
      );

    } finally {
      setReviewSubmitting(false);
    }
  };

  const reviewTags = [
    "Accurate Prediction",
    "Patient & Polite",
    "Good Guidance",
    "Helpful Remedies",
  ];

  const toggleReviewTag = (tag: string) => {
    setSelectedReviewTags((previous) =>
      previous.includes(tag)
        ? previous.filter(
            (item) => item !== tag
          )
        : [...previous, tag]
    );
  };

  // Static banner, not a real synced message — matches the astrologer
  // page's equivalent (see app/astrologer/consultation/[id]/page.tsx),
  // recomputed fresh every render so it survives a refresh instead of
  // only appearing once. The customer's own name is already known
  // locally (their own logged-in account) — no server round trip
  // needed the way the astrologer side needs one for the customer's
  // details.
  const welcomeMessage = (() => {
    if (typeof window === "undefined") return "";
    try {
      const raw = localStorage.getItem("user");
      const parsed = raw ? JSON.parse(raw) : null;
      const name = parsed?.Name || parsed?.name || "";
      const firstName = name.trim().split(/\s+/)[0];
      if (!firstName) return "";
      return `Namaste ${firstName}, Welcome to AstroEye! Hum Apki Details ke Anusar kundali dekh rahe he, Aap Kya Janana Cahte hein!`;
    } catch {
      return "";
    }
  })();

  // ---- render -----------------------------------------------------------

  return (
    <>
      <Header />

      {/* =====================================================
          CONSULTATION PAGE
      ====================================================== */}
      <main className="consultation-page" style={{ paddingTop: "100px", paddingBottom: "100px" }}>

        {/* =================================================
            LOADING
        ================================================== */}
        {phase === "loading" && (
          <section className="consultation-state-page">
            <div className="consultation-state-card">

              <div className="connecting-animation">
                <div className="connecting-ring ring-1" />
                <div className="connecting-ring ring-2" />
                <div className="connecting-ring ring-3" />

                <div className="connecting-avatar">
                  ✦
                </div>

                <span className="connecting-dot dot-1" />
                <span className="connecting-dot dot-2" />
                <span className="connecting-dot dot-3" />
                <span className="connecting-dot dot-4" />
              </div>

              <h2 className="consultation-state-title">
                Loading consultation...
              </h2>

              <p className="consultation-state-description">
                Please wait while we connect your consultation.
              </p>

            </div>
          </section>
        )}

        {/* =================================================
            NO ACTIVE CONSULTATION
        ================================================== */}
        {phase === "NONE" && (
          <section className="consultation-state-page">

            <div className="consultation-state-card">

              <div className="accepted-animation">
                <div className="accepted-circle">
                  <span className="accepted-check">
                    ✓
                  </span>
                </div>
              </div>

              <h2 className="accepted-title">
                Consultation ended
              </h2>

              <p className="accepted-description">
                This consultation has already ended. Check your wallet for the
                charge, or start a new one anytime.
              </p>

              <Link
                href="/chat-with-astrologer"
                className="consultation-start-button"
              >
                Start New Consultation
              </Link>

            </div>

          </section>
        )}

        {/* =================================================
            REQUESTED
        ================================================== */}
        {phase === "REQUESTED" && (
          <section className="consultation-state-page">

            <div className="consultation-state-card">

              {/* Connecting animation */}
              <div className="connecting-animation">

                <div className="connecting-ring ring-1" />
                <div className="connecting-ring ring-2" />
                <div className="connecting-ring ring-3" />

                <div className="connecting-avatar">
                  {astrologerName
                    ? astrologerName
                        .charAt(0)
                        .toUpperCase()
                    : "A"}
                </div>

                <span className="connecting-dot dot-1" />
                <span className="connecting-dot dot-2" />
                <span className="connecting-dot dot-3" />
                <span className="connecting-dot dot-4" />

              </div>

              {/* Title */}
              <h2 className="consultation-state-title">
                Ringing {astrologerName || "Astrologer"}...
              </h2>

              <p className="consultation-state-description">
                Waiting for them to respond
              </p>

              {/* Astrologer */}
              <div className="consultation-astrologer-name">
                {astrologerName}
              </div>

              {/* Information */}
              <div className="consultation-info-box">

                <div className="consultation-info-icon">
                  💬
                </div>

                <div>
                  <strong>
                    Connecting you to your astrologer
                  </strong>

                  <p>
                    Please wait while the astrologer
                    accepts your consultation request.
                    You will be notified as soon as
                    they respond.
                  </p>
                </div>

              </div>

              {/* Expiry */}
              <div className="consultation-expiry">

                <span>
                  Request expires in
                </span>

                <strong>
                  {formatSeconds(ringSecondsLeft)}
                </strong>

              </div>

              {/* Cancel */}
              <button
                type="button"
                className="consultation-cancel-button"
                onClick={handleCancel}
                disabled={busy}
              >
                {busy
                  ? "Cancelling..."
                  : "Cancel request"}
              </button>

            </div>

          </section>
        )}

        {/* =================================================
            ACCEPTED
        ================================================== */}
        {phase === "ACCEPTED" && (
          <section className="consultation-state-page">

            <div className="consultation-state-card">

              {/* Accepted animation */}
              <div className="accepted-animation">

                <span className="accepted-star star-1">
                  ✦
                </span>

                <span className="accepted-star star-2">
                  ✦
                </span>

                <span className="accepted-star star-3">
                  ✦
                </span>

                <div className="accepted-circle">
                  <span className="accepted-check">
                    ✓
                  </span>
                </div>

              </div>

              {/* Title */}
              <h2 className="accepted-title">
                {astrologerName || "Astrologer"} accepted your request
              </h2>

              <p className="accepted-description">
                Start your consultation now. This
                window will close in{" "}
                <strong>
                  {formatSeconds(joinSecondsLeft)}
                </strong>
                .
              </p>

              {/* Success information */}
              <div className="accepted-success-box">

                <span className="accepted-success-icon">
                  ✓
                </span>

                <span>
                  Your consultation request has been
                  accepted. You can now start chatting
                  with your astrologer.
                </span>

              </div>

              {/* Expiry */}
              <div className="accepted-expiry">

                <span>
                  Start consultation within
                </span>

                <strong>
                  {formatSeconds(joinSecondsLeft)}
                </strong>

              </div>

              {/* Start */}
              <button
                type="button"
                className="consultation-start-button"
                onClick={handleStartChat}
                disabled={busy}
              >
                {busy
                  ? "Connecting..."
                  : medium === "AUDIO"
                  ? "Start Call →"
                  : "Start Chat →"}
              </button>

            </div>

          </section>
        )}

        {/* =================================================
            ONGOING
        ================================================== */}
        {phase === "ONGOING" && (
          <section className="consultation-page-ongoing">

            <div className="chat-consultation-page">

              {medium !== "AUDIO" ? (
                <>
                  {/* ==========================================
                      CHAT HEADER
                  =========================================== */}
                  <header className="chat-header">

                    <div className="chat-astrologer">

                      <div className="chat-avatar">
                        {astrologerName
                          ? astrologerName
                              .charAt(0)
                              .toUpperCase()
                          : "A"}

                        <span />
                      </div>

                      <div>
                        <strong>
                          {astrologerName || "Astrologer"}
                        </strong>

                        <small>
                          Chat{ratePerMinute != null ? ` • ₹${ratePerMinute}/min` : ""}
                        </small>
                      </div>

                    </div>

                    <div className="chat-timer">

                      <strong>
                        {formatSeconds(remainingSeconds)}
                      </strong>

                      <small>
                        ₹{walletBalance} balance
                      </small>

                    </div>

                  </header>

                  {/* ==========================================
                      CHAT BODY
                  =========================================== */}
                  <div className="chat-body">

                    <div className="chat-background-pattern" />

                    <div className="chat-messages">

                      {welcomeMessage && (
                        <div className="chat-message-row message-other">
                          <div className="chat-message chat-message-other">
                            {welcomeMessage}
                          </div>
                        </div>
                      )}

                      {warningType && (
                        <div className="consultation-info-box">

                          <div className="consultation-info-icon">
                            !
                          </div>

                          <div>

                            <strong>
                              {warningType ===
                              "FREE_CHAT_ENDING"
                                ? "Free minutes ending"
                                : warningType ===
                                  "LOW_BALANCE"
                                ? "Low wallet balance"
                                : "Session ending"}
                            </strong>

                            <p>
                              {warningType ===
                              "FREE_CHAT_ENDING"
                                ? "Your free consultation minutes are almost finished."
                                : warningType ===
                                  "LOW_BALANCE"
                                ? "Your wallet balance is running low."
                                : "Your consultation is reaching its limit."}
                            </p>

                          </div>

                          {warningType ===
                            "FREE_CHAT_ENDING" && (
                            <button
                              type="button"
                              className="consultation-start-button"
                              onClick={() =>
                                handleContinue(true)
                              }
                            >
                              Continue
                            </button>
                          )}

                          {warningType ===
                            "LOW_BALANCE" && (
                            <button
                              type="button"
                              className="consultation-start-button"
                              onClick={
                                goToWalletThenExtend
                              }
                            >
                              Recharge
                            </button>
                          )}

                        </div>
                      )}

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

                      {messages.map((message) => (
                        <div
                          key={message.clientMessageId}
                          className={`chat-message-row ${
                            message.senderType === "USER"
                              ? "message-mine"
                              : "message-other"
                          }`}
                        >
                          <div
                            className={`chat-message ${
                              message.senderType === "USER"
                                ? "chat-message-mine"
                                : "chat-message-other"
                            } ${message.attachment ? "chat-message-media" : ""}`}
                          >
                            {message.attachment?.kind === "image" ? (
                              <a
                                href={message.attachment.url}
                                target="_blank"
                                rel="noopener noreferrer"
                              >
                                <img
                                  src={message.attachment.thumbUrl || message.attachment.url}
                                  alt="Shared photo"
                                  className="chat-message-image"
                                />
                              </a>
                            ) : message.attachment?.kind === "audio" ? (
                              <audio controls src={message.attachment.url} className="chat-message-audio" />
                            ) : isLikelyUrl(message.body) ? (
                              <a
                                href={message.body}
                                target="_blank"
                                rel="noopener noreferrer"
                                className="chat-message-link"
                              >
                                🔗 {message.body}
                              </a>
                            ) : (
                              message.body
                            )}

                            <small>
                              {message.sentAt
                                ? new Date(
                                    message.sentAt
                                  ).toLocaleTimeString(
                                    [],
                                    {
                                      hour: "2-digit",
                                      minute: "2-digit",
                                    }
                                  )
                                : ""}
                            </small>
                          </div>
                        </div>
                      ))}

                    </div>

                  </div>
                </>
              ) : (
                /* ==========================================
                    AUDIO CALL SCREEN
                =========================================== */
                <div className="astrologer-panel card-bg-primary consult-call-screen text-center py-4 my-4">
                  <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">
                    {astrologerImage ? (
                      <img
                        src={astrologerImage}
                        alt=""
                        style={{ width: "100%", height: "100%", borderRadius: "50%", objectFit: "cover" }}
                      />
                    ) : (
                      <span>{(astrologerName || "A").charAt(0).toUpperCase()}</span>
                    )}
                  </div>

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

                  <div className="consult-timer consult-timer-lg mb-1">
                    {formatSeconds(remainingSeconds)}
                  </div>
                  <p className="terms-text-color small mb-4">
                    ₹{currentCharge} spent 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>
                  </div>

                  {warningType && (
                    <div className="consult-call-warning mb-3">
                      {warningType === "FREE_CHAT_ENDING" ? (
                        <>
                          Your free minutes are ending — recharge to keep talking.{" "}
                          <button type="button" onClick={() => handleContinue(true)}>
                            Continue
                          </button>
                        </>
                      ) : warningType === "LOW_BALANCE" ? (
                        <>
                          Call will end soon; please recharge to continue.{" "}
                          <button type="button" onClick={goToWalletThenExtend}>
                            Recharge
                          </button>
                        </>
                      ) : (
                        "This call is reaching its limit."
                      )}
                    </div>
                  )}

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

              {/* ==========================================
                  MESSAGE INPUT
              =========================================== */}
              {medium !== "AUDIO" && (
                <div className="chat-input-wrapper">

                  <button
                    type="button"
                    className="chat-add-button"
                    onClick={() =>
                      setShowAttachmentSheet(true)
                    }
                    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... {formatSeconds(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>
                  ) : (
                    <>
                      <form
                        className="chat-input-form"
                        onSubmit={(event) => {
                          event.preventDefault();
                          handleSend();
                        }}
                      >

                        <input
                          type="text"
                          placeholder={
                            voiceTyping.isListening
                              ? "Listening..."
                              : "Type a reply"
                          }
                          value={composerValue}
                          onChange={(event) =>
                            setComposerValue(
                              event.target.value
                            )
                          }
                        />

                        <button
                          type="button"
                          className={`chat-dictation-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="submit"
                          className="chat-send-button"
                          disabled={!composerValue.trim()}
                        >
                          ↑
                        </button>

                      </form>

                      <button
                        type="button"
                        className="chat-add-button"
                        onClick={startVoiceRecording}
                        disabled={sendingImage || sendingVoice}
                        aria-label="Record a voice message"
                        title="Record a voice message"
                      >
                        🎤
                      </button>
                    </>
                  )}

                </div>
              )}

              {/* ==========================================
                  END CONSULTATION
                  (AUDIO already has its own "End call" button
                  inside the call screen card above — rendering
                  this one too would duplicate it.)
              =========================================== */}
              {medium !== "AUDIO" && (
                <div className="chat-end-wrapper">

                  <button
                    type="button"
                    className="chat-end-button"
                    onClick={() => setShowEndConfirm(true)}
                    disabled={busy}
                  >
                    ⊗{" "}
                    {busy
                      ? "Ending consultation..."
                      : "End consultation"}
                  </button>

                </div>
              )}

            </div>

          </section>
        )}

        {/* =================================================
            MISSED
        ================================================== */}
        {phase === "MISSED" && (
          <section className="consultation-state-page">

            <div className="consultation-state-card">

              <h2 className="consultation-state-title">
                No response
              </h2>

              <p className="consultation-state-description">
                {astrologerName || "The astrologer"} didn't
                respond in time.
              </p>

              <Link
                href="/chat-with-astrologer"
                className="consultation-start-button"
              >
                Try another astrologer
              </Link>

            </div>

          </section>
        )}

        {/* =================================================
            REJECTED
        ================================================== */}
        {phase === "REJECTED" && (
          <section className="consultation-state-page">

            <div className="consultation-state-card">

              <h2 className="consultation-state-title">
                Astrologer unavailable
              </h2>

              <p className="consultation-state-description">
                {astrologerName || "The astrologer"} isn't
                available right now.
              </p>

              <Link
                href="/chat-with-astrologer"
                className="consultation-start-button"
              >
                Try another astrologer
              </Link>

            </div>

          </section>
        )}

        {/* =================================================
            CANCELLED
        ================================================== */}
        {phase === "CANCELLED" && (
          <section className="consultation-state-page">

            <div className="consultation-state-card">

              <h2 className="consultation-state-title">
                Request cancelled
              </h2>

              <p className="consultation-state-description">
                Request cancelled — nothing was charged.
              </p>

              <Link
                href="/chat-with-astrologer"
                className="consultation-start-button"
              >
                Back to Astrologers
              </Link>

            </div>

          </section>
        )}

        {/* =================================================
            COMPLETED
        ================================================== */}
        {phase === "COMPLETED" && (
          <section className="consultation-state-page">

            <div className="consultation-state-card">

              <div className="accepted-animation">

                <div className="accepted-circle" style={{ background: "#22C55E" }}>
                  <span className="accepted-check">
                    ✓
                  </span>
                </div>

              </div>

              <h2 className="accepted-title">
                {medium === "AUDIO" ? "Call" : "Chat"} Completed
              </h2>

              <p className="terms-text-color small mb-4">
                Your {medium === "AUDIO" ? "call" : "chat"} with{" "}
                {astrologerName || "the astrologer"} has ended.
              </p>

              {receipt ? (
                <>
                  <div className="astrologer-panel card-bg-primary text-start mb-3 receipt-card">
                    <h6 className="fw-bold mb-3">
                      {medium === "AUDIO" ? "Call" : "Chat"} Details
                    </h6>

                    <div className="receipt-row">
                      <span className="terms-text-color small">📅 Date</span>
                      <span className="fw-semibold">
                        {formatDatePart(startedAt || receipt.ended_at)}
                      </span>
                    </div>

                    <div className="receipt-row">
                      <span className="terms-text-color small">🕐 Start Time</span>
                      <span className="fw-semibold">{formatTimePart(startedAt)}</span>
                    </div>

                    <div className="receipt-row">
                      <span className="terms-text-color small">🕐 End Time</span>
                      <span className="fw-semibold">{formatTimePart(receipt.ended_at)}</span>
                    </div>

                    <div className="receipt-row">
                      <span className="terms-text-color small">⏳ Duration</span>
                      <span className="fw-semibold">{receipt.duration || "-"}</span>
                    </div>
                  </div>

                  <div className="astrologer-panel card-bg-primary text-start mb-4 receipt-card">
                    <div className="d-flex justify-content-between align-items-center mb-3">
                      <h6 className="fw-bold mb-0">Charges Details</h6>
                      {(receipt.deducted_amount ?? 0) > 0 && (
                        <span className="badge bg-warning text-dark">Auto-deducted</span>
                      )}
                    </div>

                    <div className="receipt-row">
                      <span className="terms-text-color small">₹ Rate</span>
                      <span className="fw-semibold">
                        {receipt.rate_per_minute != null ? `₹${receipt.rate_per_minute}/min` : "-"}
                      </span>
                    </div>

                    <div className="receipt-row">
                      <span className="terms-text-color small">⏱ Total Duration</span>
                      <span className="fw-semibold">
                        {receipt.duration_seconds != null
                          ? `${(receipt.duration_seconds / 60).toFixed(1)} min`
                          : receipt.duration || "-"}
                      </span>
                    </div>

                    <div className="receipt-row">
                      <span className="terms-text-color small">Total Charge</span>
                      <span className="fw-semibold">
                        {receipt.deducted_amount ? `₹${receipt.deducted_amount}` : "Free — ₹0"}
                      </span>
                    </div>

                    <div className="receipt-row">
                      <span className="terms-text-color small">Wallet Deducted</span>
                      <span className="fw-semibold" style={{ color: "#22C55E" }}>
                        {receipt.deducted_amount ? `₹${receipt.deducted_amount}` : "₹0"}
                      </span>
                    </div>
                  </div>

                  <p className="terms-text-color small mb-4">
                    Thank you for using <span style={{ color: "#FFC107" }}>AstroEye</span>!
                  </p>
                </>
              ) : (
                <p className="consultation-state-description">
                  Session ended.
                </p>
              )}

              <Link
                href="/"
                className="consultation-start-button"
              >
                Back to Home
              </Link>

            </div>

          </section>
        )}

      </main>
        {/* =====================================================
            END CHAT CONFIRMATION
        ===================================================== */}

        {showEndConfirm && (
          <div className="consultation-modal-overlay">

            <div className="end-chat-modal">

              <button
                type="button"
                className="modal-close-button"
                onClick={() =>
                  setShowEndConfirm(false)
                }
              >
                ×
              </button>

              <div className="end-chat-warning-icon">
                !
              </div>

              <h2>
                {medium === "AUDIO" ? "End Call?" : "End Chat?"}
              </h2>

              <p>
                Are you sure you want to end the {medium === "AUDIO" ? "call" : "chat"}?
              </p>

              <p className="modal-subtext">
                Your session will be closed.
              </p>

              <div className="end-chat-actions">

                <button
                  type="button"
                  className="end-chat-cancel"
                  onClick={() =>
                    setShowEndConfirm(false)
                  }
                >
                  Cancel
                </button>

                <button
                  type="button"
                  className="end-chat-confirm"
                  onClick={handleEnd}
                  disabled={busy}
                >
                  {busy
                    ? "Ending..."
                    : medium === "AUDIO"
                      ? "End Call"
                      : "End Chat"}
                </button>

              </div>

            </div>

          </div>
        )}
        {/* =====================================================
            CHAT SESSION ENDED / REVIEW
        ===================================================== */}

        {showReview && (
          <div className="consultation-modal-overlay">

            <div className="chat-review-modal">

              {!reviewSubmitted ? (
                <>
                  {/* CLOSE */}

                  <button
                    type="button"
                    className="modal-close-button"
                    onClick={() =>
                      setShowReview(false)
                    }
                  >
                    ×
                  </button>

                  {/* ICON */}

                  <div className="review-chat-icon">
                    💬
                  </div>

                  {/* TITLE */}

                  <h2>
                    Chat Session Ended
                  </h2>

                  <p className="review-description">
                    Thank you for chatting with{" "}
                    <strong>
                      {astrologerName}
                    </strong>
                  </p>

                  {/* STARS */}

                  <div className="review-stars">

                    {[1, 2, 3, 4, 5].map(
                      (star) => (
                        <button
                          type="button"
                          key={star}
                          className={
                            star <= reviewRating
                              ? "review-star active"
                              : "review-star"
                          }
                          onClick={() =>
                            setReviewRating(star)
                          }
                        >
                          ★
                        </button>
                      )
                    )}

                  </div>

                  <div className="review-rating-label">
                    {reviewRating > 0
                      ? "Rate your consultation"
                      : "Tap a star to rate"}
                  </div>

                  {/* TAGS */}

                  <div className="review-tags">

                    {reviewTags.map((tag) => (
                      <button
                        type="button"
                        key={tag}
                        className={
                          selectedReviewTags.includes(
                            tag
                          )
                            ? "review-tag selected"
                            : "review-tag"
                        }
                        onClick={() =>
                          toggleReviewTag(tag)
                        }
                      >
                        {tag}
                      </button>
                    ))}

                  </div>

                  {/* REVIEW */}

                  <textarea
                    className="review-textarea"
                    placeholder="Write your review here... (e.g. how was your consultation?)"
                    value={reviewText}
                    onChange={(event) =>
                      setReviewText(
                        event.target.value
                      )
                    }
                  />

                  {/* ERROR */}

                  {reviewError && (
                    <div className="review-error">
                      {reviewError}
                    </div>
                  )}

                  {/* SUBMIT */}

                  <button
                    type="button"
                    className="review-submit-button"
                    onClick={submitUserReview}
                    disabled={reviewSubmitting}
                  >
                    {reviewSubmitting
                      ? "Submitting..."
                      : "Submit Review"}
                  </button>

                  {/* SKIP */}

                  <button
                    type="button"
                    className="review-skip-button"
                    onClick={() => setShowReview(false)}
                  >
                    Skip
                  </button>
                </>
              ) : (
                /* =========================================
                  REVIEW SUCCESS
                ========================================== */

                <div className="review-success">

                  <div className="review-success-icon">
                    ✓
                  </div>

                  <h2>
                    Thank You!
                  </h2>

                  <p>
                    Your review has been submitted
                    successfully.
                  </p>

                  <button
                    type="button"
                    className="review-submit-button"
                    onClick={() => setShowReview(false)}
                  >
                    Continue
                  </button>

                </div>
              )}

            </div>

          </div>
        )}

        {/* =====================================================
            ATTACHMENT SHEET
        ===================================================== */}

        {showAttachmentSheet && (
          <div
            className="consultation-modal-overlay attachment-overlay"
            onClick={() => {
              setShowAttachmentSheet(false);
              setShowLinkInput(false);
            }}
          >

            <div
              className="attachment-sheet"
              onClick={(event) =>
                event.stopPropagation()
              }
            >

              <div className="attachment-header">

                <div>
                  <h3>
                    {showLinkInput ? "Share a link" : "Send something"}
                  </h3>

                  <p>
                    {showLinkInput
                      ? "Sent as a real message — the other person can open it directly."
                      : "Attachments stay in this thread's history."}
                  </p>
                </div>

                <button
                  type="button"
                  onClick={() => {
                    setShowAttachmentSheet(false);
                    setShowLinkInput(false);
                  }}
                >
                  ×
                </button>

              </div>

              {showLinkInput ? (
                <form
                  className="attachment-link-form"
                  onSubmit={(event) => {
                    event.preventDefault();
                    handleSendLink();
                  }}
                >
                  <input
                    type="url"
                    autoFocus
                    placeholder="https://example.com"
                    value={linkDraft}
                    onChange={(event) => setLinkDraft(event.target.value)}
                  />

                  <div className="attachment-link-actions">
                    <button
                      type="button"
                      className="btn btn-outline-light"
                      onClick={() => setShowLinkInput(false)}
                    >
                      Back
                    </button>
                    <button
                      type="submit"
                      className="btn btn-warning fw-bold"
                      disabled={!linkDraft.trim()}
                    >
                      Send
                    </button>
                  </div>
                </form>
              ) : (
                <div className="attachment-grid">

                  {/* PHOTO — a real Agora Chat image message (see
                      handlePickImage/handleImageSelected). */}

                  <button
                    type="button"
                    className="attachment-option"
                    onClick={handlePickImage}
                  >
                    <span className="attachment-icon">
                      ▧
                    </span>

                    <strong>
                      Photo
                    </strong>

                    <small>
                      Camera or gallery
                    </small>
                  </button>

                  {/* REMEDIES */}

                  <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>

                  {/* LINK */}

                  <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>
        )}
      <Footer />
    </>
  );

  function goToWalletThenExtend() {
    router.push("/add-wallet-money/price-list");
  }
}
