import React, { useEffect, useState } from "react";
import Linkified from "@/shared/Linkified";
import axios from "axios";
import Icon from "./Icon";
import { fmtBytes, fmtHKD, fmtTime, fmtWhen } from "./format";
import { MESSAGE_CHANNEL_LABEL } from "@/shared/status";

// ONE message from a vendor conversation, drawn the same way wherever it appears.
//
// It used to live inside ChatTab, which meant the room could draw a reschedule card and the guided
// chat could not. That was not a cosmetic difference: the two surfaces show the SAME room, so a
// customer sitting in the guided chat simply never saw a question their vendor had asked, and the
// booking-status announcements and the vendor's own Inbox replies were invisible to them too. The
// renderer returned null for anything it did not recognise, so the message did not merely look
// wrong — it was not there. (Arfu, 02/09/2026)
//
// `mine` is a PROP rather than something read off the message, because the two surfaces disagree
// about the column: /thread/get flips direction into the reader's point of view (their own messages
// become "outbound"), while a flow run reports the raw engine value, where "outbound" means the bot
// spoke. Reading `m.direction` here would put every message on the correct side of one surface and
// the wrong side of the other.
// `data-mid` on every root: a notification names the MESSAGE it is about, and the only way to take
// somebody to it is to find it in the DOM. (Arfu, 02/09/2026)
export default function RoomMessage({ message: m, messages = [], mine = false, onPickReceipt, onPickChoice, onPickReschedule, onAnswerFlow }) {
            const isOut = !!mine;
            if (m.kind === "image") {
                return (
                    <div key={m.id || i} data-mid={m.id} className={`wg-row ${isOut ? "out" : "in"}`}>
                        <div>
                            <a className="wg-img-bub" href={m.payload?.url} target="_blank" rel="noreferrer">
                                <img src={m.payload?.url} alt={m.payload?.name || ""} />
                            </a>
                            <div className="wg-time" style={{ textAlign: isOut ? "right" : "left" }}>{fmtTime(m.created_at)}</div>
                        </div>
                    </div>
                );
            }
            if (m.kind === "file") {
                return (
                    <div key={m.id || i} data-mid={m.id} className={`wg-row ${isOut ? "out" : "in"}`}>
                        <div>
                            <a className={`wg-file-bub ${isOut ? "out" : "in"}`} href={m.payload?.url} target="_blank" rel="noreferrer" download={m.payload?.name}>
                                <span className="wg-file-ic"><Icon name="file" size={18} /></span>
                                <div className="wg-file-meta">
                                    <div className="wg-file-name">{m.payload?.name}</div>
                                    <div className="wg-file-size">{fmtBytes(m.payload?.size)}</div>
                                </div>
                            </a>
                            <div className="wg-time" style={{ textAlign: isOut ? "right" : "left" }}>{fmtTime(m.created_at)}</div>
                        </div>
                    </div>
                );
            }
            if (m.kind === "booking_confirmed") {
                return (
                    <div key={m.id || i} data-mid={m.id} className="wg-row in">
                        <div style={{ maxWidth: "88%" }}>
                            <div className="wg-card">
                                <div className="wg-card-hd ok"><Icon name="checkc" size={12} stroke={3} /> Booking confirmed</div>
                                <div className="wg-card-bd">
                                    <div className="wg-kv"><span>Service</span><b>{m.payload?.service}</b></div>
                                    <div className="wg-kv"><span>When</span><b>{fmtWhen(m.payload?.when)}</b></div>
                                    {m.payload?.price ? <div className="wg-kv"><span>Price</span><b>{fmtHKD(m.payload.price)}</b></div> : null}
                                    {/* The STORED reference. This used to render BK-{booking_id}, so the
                                        customer saw one reference and the org searched for another. */}
                                    {m.payload?.booking_ref ? <div className="wg-kv"><span>Reference</span><b className="mono">{m.payload.booking_ref}</b></div> : null}
                                </div>
                            </div>
                            <div style={{ fontSize: 10, color: "#a4858a", marginTop: 4 }}>{fmtTime(m.created_at)}</div>
                        </div>
                    </div>
                );
            }
            // The engagement's status moved (T14) — confirmed, completed, cancelled. Arrives
            // through the same idempotent announcement path as everything else that crosses
            // from MySQL into the transcript.
            if (m.kind === "booking_status") {
                const cancelled = /cancel/i.test(m.payload?.status || "");
                return (
                    <div key={m.id || i} data-mid={m.id} className="wg-row in">
                        <div style={{ maxWidth: "88%" }}>
                            <div className="wg-card">
                                <div className={`wg-card-hd ${cancelled ? "" : "ok"}`}>
                                    <Icon name={cancelled ? "x" : "checkc"} size={12} stroke={3} /> {m.payload?.status}
                                </div>
                                <div className="wg-card-bd">
                                    <div className="wg-kv"><span>Service</span><b>{m.payload?.service}</b></div>
                                    {m.payload?.when ? <div className="wg-kv"><span>When</span><b>{fmtWhen(m.payload.when)}</b></div> : null}
                                    {m.payload?.booking_ref ? <div className="wg-kv"><span>Reference</span><b className="mono">{m.payload.booking_ref}</b></div> : null}
                                </div>
                            </div>
                            <div style={{ fontSize: 10, color: "#a4858a", marginTop: 4 }}>{fmtTime(m.created_at)}</div>
                        </div>
                    </div>
                );
            }
            // A scheduled meeting reminding itself (T11). No flow produced it, so it arrives
            // in the room transcript rather than in FlowChat.
            if (m.kind === "appointment_reminder") {
                return (
                    <div key={m.id || i} data-mid={m.id} className="wg-row in">
                        <div style={{ maxWidth: "88%" }}>
                            <div className="wg-card">
                                <div className="wg-card-hd"><Icon name="clock" size={12} stroke={3} /> Reminder</div>
                                <div className="wg-card-bd">
                                    <div className="wg-kv"><span>Service</span><b>{m.payload?.service}</b></div>
                                    <div className="wg-kv"><span>When</span><b>{fmtWhen(m.payload?.when)}</b></div>
                                    {m.payload?.booking_ref ? <div className="wg-kv"><span>Reference</span><b className="mono">{m.payload.booking_ref}</b></div> : null}
                                </div>
                            </div>
                            <div style={{ fontSize: 10, color: "#a4858a", marginTop: 4 }}>{fmtTime(m.created_at)}</div>
                        </div>
                    </div>
                );
            }
            if (m.kind === "invoice") {
                const noteText = m.payload?.note?.trim();
                const serviceText = m.payload?.service ? ` for ${m.payload.service}` : "";
                const bubbleText = noteText
                    ? noteText
                    : `Please pay ${fmtHKD(m.payload?.amount)}${serviceText}.`;
                return (
                    <div key={m.id || i} data-mid={m.id} className="wg-row in">
                        <div style={{ maxWidth: "88%" }}>
                            <div className="wg-bub">
                                <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 6, color: "var(--brand-700)", fontSize: 11.5, fontWeight: 700, textTransform: "uppercase", letterSpacing: ".05em" }}>
                                    <Icon name="card" size={13} /> Invoice · {fmtHKD(m.payload?.amount)}
                                </div>
                                <div style={{ whiteSpace: "pre-wrap" }}>{bubbleText}</div>
                            </div>
                            <div className="wg-time" style={{ textAlign: "left" }}>{fmtTime(m.created_at)}</div>
                            <button type="button"
                                onClick={() => onPickReceipt?.(m.payload?.payment_id)}
                                style={{
                                    marginTop: 8, display: "inline-flex", alignItems: "center", gap: 7,
                                    padding: "9px 14px", borderRadius: 999, background: "var(--brand)",
                                    color: "#fff", border: "none", fontWeight: 700, fontSize: 13,
                                    cursor: "pointer", fontFamily: "var(--font)",
                                    boxShadow: "0 4px 12px rgba(191,53,80,.28)",
                                }}>
                                <Icon name="paperclip" size={14} /> Upload receipt
                            </button>
                        </div>
                    </div>
                );
            }
            if (m.kind === "receipt_uploaded") {
                return (
                    <div key={m.id || i} data-mid={m.id} className={`wg-row ${isOut ? "out" : "in"}`}>
                        <div style={{ maxWidth: "88%" }}>
                            <div className="wg-card" style={{ borderColor: "#c98a14" }}>
                                <div className="wg-card-hd" style={{ background: "linear-gradient(180deg,#fbf0d8,#fff)", color: "#b07a14" }}>
                                    <Icon name="checkc" size={12} stroke={3} /> Receipt sent · awaiting verification
                                </div>
                                <div className="wg-card-bd">
                                    <div className="wg-kv"><span>Amount</span><b>{fmtHKD(m.payload?.amount)}</b></div>
                                    <div className="wg-kv"><span>File</span><b className="mono">{m.payload?.file_name}</b></div>
                                </div>
                            </div>
                            <div style={{ fontSize: 10, color: "#a4858a", marginTop: 4 }}>{fmtTime(m.created_at)}</div>
                        </div>
                    </div>
                );
            }
            if (m.kind === "payment_verified") {
                return (
                    <div key={m.id || i} data-mid={m.id} className="wg-row in">
                        <div style={{ maxWidth: "88%" }}>
                            <div className="wg-card" style={{ borderColor: "#2f9e6f" }}>
                                <div className="wg-card-hd ok"><Icon name="checkc" size={12} stroke={3} /> Payment verified</div>
                                <div className="wg-card-bd">
                                    <div className="wg-kv"><span>Amount</span><b>{fmtHKD(m.payload?.amount)}</b></div>
                                    {m.payload?.service ? <div className="wg-kv"><span>For</span><b>{m.payload.service}</b></div> : null}
                                </div>
                            </div>
                            <div style={{ fontSize: 10, color: "#a4858a", marginTop: 4 }}>{fmtTime(m.created_at)}</div>
                        </div>
                    </div>
                );
            }
            if (m.kind === "reschedule_request") {
                const confirmed = messages.find(other => other.payload?.type === "reschedule_confirmed" && other.payload?.request_id === m.id);
                return (
                    <div key={m.id || i} data-mid={m.id} className="wg-row in">
                        <div style={{ maxWidth: "92%" }}>
                            <div className="wg-card" style={{ borderColor: "#c98a14" }}>
                                <div className="wg-card-hd" style={{ background: "linear-gradient(180deg,#fdf2dc,#fff)", color: "#b07a14" }}>
                                    <Icon name="calendar" size={12} /> Pick a new slot
                                </div>
                                <div className="wg-card-bd">
                                    <div style={{ fontSize: 13, color: "#534045", lineHeight: 1.5, marginBottom: 8, whiteSpace: "pre-wrap" }}><Linkified text={m.payload?.text} /></div>
                                    {m.payload?.old_when ? (
                                        <div className="wg-kv"><span>Currently</span><b>{fmtWhen(m.payload.old_when)}</b></div>
                                    ) : null}
                                    {m.payload?.booking_ref ? (
                                        <div className="wg-kv"><span>Booking</span><b className="mono">{m.payload.booking_ref}</b></div>
                                    ) : null}
                                    <RescheduleSlots serviceId={m.payload?.service_id} confirmed={confirmed}
                                        onPick={(iso) => onPickReschedule?.(m, iso)} />
                                </div>
                            </div>
                            <div style={{ fontSize: 10, color: "#a4858a", marginTop: 4 }}>{fmtTime(m.created_at)}</div>
                        </div>
                    </div>
                );
            }
            if (m.kind === "reschedule_confirmed") {
                return (
                    <div key={m.id || i} data-mid={m.id} className={`wg-row ${isOut ? "out" : "in"}`}>
                        <div style={{ maxWidth: "88%" }}>
                            <div className="wg-card" style={{ borderColor: "#2f9e6f" }}>
                                <div className="wg-card-hd ok"><Icon name="checkc" size={12} stroke={3} /> Rescheduled</div>
                                <div className="wg-card-bd">
                                    <div className="wg-kv"><span>From</span><b style={{ textDecoration: "line-through", color: "#a4858a" }}>{fmtWhen(m.payload?.old_when)}</b></div>
                                    <div className="wg-kv"><span>To</span><b>{fmtWhen(m.payload?.new_when)}</b></div>
                                    {m.payload?.booking_ref ? <div className="wg-kv"><span>Booking</span><b className="mono">{m.payload.booking_ref}</b></div> : null}
                                </div>
                            </div>
                            <div style={{ fontSize: 10, color: "#a4858a", marginTop: 4 }}>{fmtTime(m.created_at)}</div>
                        </div>
                    </div>
                );
            }
            // A CHATFLOW'S OWN QUESTION, answerable where it is asked (17/09/2026). Same card as the
            // org's ad-hoc prompt below, but the answer goes to the RUN rather than into the room:
            // picking an option advances the flow. Answered when a `user_input` for the same node
            // follows it, which is what the engine writes back.
            if (m.kind === "choice") {
                const answered = messages.find(o => o.kind === "user_input" && o.node_id === m.node_id);
                const options = m.payload?.options || [];

                return (
                    <div key={m.id || i} data-mid={m.id} className="wg-row in">
                        <div style={{ maxWidth: "88%" }}>
                            <div className="wg-bub">
                                <div style={{ whiteSpace: "pre-wrap" }}><Linkified text={m.payload?.prompt || m.payload?.text} /></div>
                                <div style={{ display: "flex", flexWrap: "wrap", gap: 6, marginTop: 8 }}>
                                    {options.map((opt, oi) => {
                                        const value = typeof opt === "string" ? opt : opt.value;
                                        const label = typeof opt === "string" ? opt : (opt.label ?? opt.value);
                                        const picked = answered?.payload?.value === value;
                                        return (
                                            <button key={oi} type="button"
                                                onClick={answered || !onAnswerFlow ? undefined : () => onAnswerFlow(m, value)}
                                                disabled={!!answered || !onAnswerFlow}
                                                style={{
                                                    padding: "8px 14px", borderRadius: 999, fontWeight: 700, fontSize: 13,
                                                    cursor: answered ? "default" : "pointer", fontFamily: "var(--font)",
                                                    border: `1px solid ${picked ? "var(--brand)" : "#efd9dd"}`,
                                                    background: picked ? "var(--brand)" : "#fff",
                                                    color: picked ? "#fff" : (answered ? "#b9a6aa" : "var(--brand)"),
                                                }}>
                                                {label}
                                            </button>
                                        );
                                    })}
                                </div>
                            </div>
                            <div style={{ fontSize: 10, color: "#a4858a", marginTop: 4 }}>{fmtTime(m.created_at)}</div>
                        </div>
                    </div>
                );
            }
            if (m.kind === "user_input") {
                return (
                    <div key={m.id || i} data-mid={m.id} className="wg-row out">
                        <div style={{ maxWidth: "88%" }}>
                            <div className="wg-bub"><Linkified text={m.payload?.text || m.payload?.value} /></div>
                            <div style={{ fontSize: 10, color: "#a4858a", marginTop: 4, textAlign: "right" }}>{fmtTime(m.created_at)}</div>
                        </div>
                    </div>
                );
            }
            if (m.kind === "choice_request") {
                const answered = messages.find(other => other.payload?.type === "choice_response" && other.payload?.request_id === m.id);
                return (
                    <div key={m.id || i} data-mid={m.id} className="wg-row in">
                        <div style={{ maxWidth: "88%" }}>
                            <div className="wg-bub">
                                <div style={{ whiteSpace: "pre-wrap" }}><Linkified text={m.payload?.text} /></div>
                            </div>
                            <div style={{ display: "flex", flexWrap: "wrap", gap: 6, marginTop: 8 }}>
                                {(m.payload?.options || []).map((opt, oi) => {
                                    const picked = answered?.payload?.choice === opt;
                                    return (
                                        <button
                                            key={oi}
                                            type="button"
                                            onClick={answered ? undefined : () => onPickChoice?.(m, opt)}
                                            disabled={!!answered}
                                            style={{
                                                padding: "8px 14px", borderRadius: 999, fontWeight: 700, fontSize: 13,
                                                fontFamily: "var(--font)", cursor: answered ? "default" : "pointer",
                                                border: picked ? "1.5px solid var(--green)" : "1.5px solid #efe2e4",
                                                background: picked ? "#e7f5ee" : (answered ? "#f3eaeb" : "#fff"),
                                                color: picked ? "#1f8a5b" : (answered ? "#a4858a" : "var(--brand)"),
                                            }}>
                                            {picked ? "✓ " : ""}{opt}
                                        </button>
                                    );
                                })}
                            </div>
                            <div className="wg-time" style={{ textAlign: "left", marginTop: 4 }}>{fmtTime(m.created_at)}</div>
                        </div>
                    </div>
                );
            }
            if (m.kind === "choice_response") {
                return (
                    <div key={m.id || i} data-mid={m.id} className={`wg-row ${isOut ? "out" : "in"}`}>
                        <div>
                            <div className="wg-bub">↳ You chose <b>{m.payload?.choice}</b></div>
                            <div className="wg-time" style={{ textAlign: isOut ? "right" : "left" }}>{fmtTime(m.created_at)}</div>
                        </div>
                    </div>
                );
            }
            if (m.kind === "payment_rejected") {
                return (
                    <div key={m.id || i} data-mid={m.id} className="wg-row in">
                        <div style={{ maxWidth: "88%" }}>
                            <div className="wg-card" style={{ borderColor: "#c5384b" }}>
                                <div className="wg-card-hd" style={{ background: "linear-gradient(180deg,#fbe7ea,#fff)", color: "#c5384b" }}>
                                    <Icon name="x" size={12} stroke={3} /> Payment rejected
                                </div>
                                <div className="wg-card-bd">
                                    <div className="wg-kv"><span>Amount</span><b>{fmtHKD(m.payload?.amount)}</b></div>
                                    {m.payload?.reason ? (
                                        <div style={{ fontSize: 12.5, color: "#534045", marginTop: 6, whiteSpace: "pre-wrap" }}>
                                            <b style={{ color: "#231417" }}>Reason:</b> {m.payload.reason}
                                        </div>
                                    ) : null}
                                    <button type="button"
                                        onClick={() => onPickReceipt?.(m.payload?.payment_id)}
                                        style={{
                                            marginTop: 10, width: "100%", height: 38, borderRadius: 10,
                                            background: "var(--brand)", color: "#fff", border: "none",
                                            fontWeight: 700, fontSize: 13, cursor: "pointer", fontFamily: "var(--font)",
                                        }}>
                                        Upload a new receipt
                                    </button>
                                </div>
                            </div>
                            <div style={{ fontSize: 10, color: "#a4858a", marginTop: 4 }}>{fmtTime(m.created_at)}</div>
                        </div>
                    </div>
                );
            }
            const atts = Array.isArray(m.payload?.attachments) ? m.payload.attachments : [];
            return (
                <div key={m.id || i} data-mid={m.id} className={`wg-row ${isOut ? "out" : "in"}`}>
                    <div>
                        {m.payload?.text ? <div className="wg-bub"><Linkified text={m.payload.text} /></div> : null}
                        {atts.map((a, ai) => {
                            const gap = (ai > 0 || m.payload?.text) ? 6 : 0;
                            // WITHDRAWN IS NOT DELETED. The vendor took this document back, so the
                            // message stays — a conversation is a record — and the card stops being
                            // a door: it was still offering a link, and the guard behind it answers
                            // 404 to a withdrawn document. (Arfu, 23/09/2026)
                            if (a.withdrawn) {
                                return (
                                    <div key={ai} className="wg-file-bub gone" style={{ marginTop: gap }}>
                                        <span className="wg-file-ic"><Icon name="file" size={18} /></span>
                                        <div className="wg-file-meta">
                                            <div className="wg-file-name">{a.name}</div>
                                            <div className="wg-file-size">Withdrawn by the vendor</div>
                                        </div>
                                    </div>
                                );
                            }

                            return a.kind === "image" ? (
                                <a key={ai} className="wg-img-bub" href={a.url} target="_blank" rel="noreferrer" style={{ marginTop: gap }}>
                                    <img src={a.url} alt={a.name || ""} />
                                </a>
                            ) : (
                                <a key={ai} className={`wg-file-bub ${isOut ? "out" : "in"}`} href={a.url} target="_blank" rel="noreferrer" download={a.name} style={{ marginTop: gap }}>
                                    <span className="wg-file-ic"><Icon name="file" size={18} /></span>
                                    <div className="wg-file-meta">
                                        <div className="wg-file-name">{a.name}</div>
                                        <div className="wg-file-size">{fmtBytes(a.size)}</div>
                                    </div>
                                </a>
                            );
                        })}
                        {/* WHERE THIS LINE WENT (FP-T4, A8). Drawn only when it is NOT the app: a marker on
                            every in-app message would be noise on the ordinary case, and the point is that
                            one conversation holds both. */}
                        <div className="wg-time" style={{ textAlign: isOut ? "right" : "left" }}>
                            {MESSAGE_CHANNEL_LABEL[m.channel] ? `${MESSAGE_CHANNEL_LABEL[m.channel]} · ` : ""}
                            {fmtTime(m.created_at)}
                        </div>
                    </div>
                </div>
            );
}

function RescheduleSlots({ serviceId, confirmed, onPick }) {
    const { loading, slots, failed } = useOpenSlots(serviceId);

    return (
        <div style={{ marginTop: 10 }}>
            <div className="wg-detail-h" style={{ marginBottom: 8 }}>{confirmed ? "You picked" : "Choose a slot"}</div>
            {/* A request sent before the payload carried service_id cannot be answered here —
                saying so beats offering times the vendor never published. */}
            {!serviceId ? (
                <div className="wg-li-s">Reply in the chat with a time that suits you.</div>
            ) : loading ? (
                <div className="wg-li-s">Loading available times…</div>
            ) : failed ? (
                // NOT "no open times": we do not know that. Saying so would tell the customer
                // something about the vendor that a dropped request cannot support.
                <div className="wg-li-s">Could not load times just now. Pull to refresh, or reply here and your vendor can arrange one.</div>
            ) : slots.length === 0 ? (
                <div className="wg-li-s">No open times right now — reply here and your vendor can arrange one.</div>
            ) : (
                <div className="wg-slots">
                    {slots.map(s => {
                        const picked = confirmed?.payload?.new_when === s.iso;
                        return (
                            <button key={s.id}
                                className={`wg-slot ${picked ? "on" : ""}`}
                                onClick={confirmed ? undefined : () => onPick(s.iso)}
                                disabled={!!confirmed}
                                style={{ cursor: confirmed ? "default" : "pointer", opacity: confirmed && !picked ? .55 : 1 }}>
                                {picked ? "\u2713 " : ""}{fmtWhen(s.iso)}
                            </button>
                        );
                    })}
                </div>
            )}
        </div>
    );
}

// Open times come from the server: GET /availability/list, the same source the flow's
// slot_picker reads, computed from the resource's weekly windows, the service duration and what
// is already booked. It used to be buildSlots() — six times hardcoded in this file, identical for
// every service, unrelated to when the vendor actually works, and bookable.
function useOpenSlots(serviceId) {
    // `failed` is separate from an empty list on purpose. Every error used to land in the same
    // `slots: []`, so a request that never arrived — signed out, offline, a 500 — read to the
    // customer as "your vendor has no free times", which is a different and much worse statement.
    // (Arfu, 02/09/2026)
    const [state, setState] = useState({ loading: true, slots: [], failed: false });

    useEffect(() => {
        if (!serviceId) { setState({ loading: false, slots: [], failed: false }); return; }

        let live = true;
        setState({ loading: true, slots: [], failed: false });

        axios.get("/availability/list", { params: { service_id: serviceId } })
            // The endpoint answers with the { data, message, status } envelope, so the slots are at
            // .data.data.slots. This read .data.slots — the envelope itself — which is undefined,
            // so EVERY reschedule card said "No open times right now" no matter how free the vendor
            // was. It went unnoticed because the flow's own picker goes through chat/client.js,
            // which unwraps correctly; only this copy did not. The `?? resp.data` keeps a bare
            // response working too. (Arfu, 02/09/2026)
            .then(({ data: body }) => live && setState({
                loading: false,
                failed: false,
                // The picker speaks in ISO strings; the resource the slot belongs to rides along
                // for whoever needs it.
                slots: ((body?.data ?? body)?.slots || []).map(slot => ({
                    id: `${slot.resource_id}-${slot.starts_at}`,
                    iso: slot.starts_at,
                    resourceId: slot.resource_id,
                })),
            }))
            .catch(() => live && setState({ loading: false, slots: [], failed: true }));

        return () => { live = false; };
    }, [serviceId]);

    return state;
}
