import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { FaSpinner } from "react-icons/fa";
import TaleButton from "@components/TaleButton";
import { TaleInput } from "@components/TaleInput";
import {
    AGENT_LAST_PHASE,
    isTechnicalLine,
    openQuestionnaire,
    parseOptions,
    parsePhase,
} from "@/api/agent";
import { useTranslation } from "@/hooks/useTranslation";

/**
 * The guided conversation: the Tale agent, over its own WebSocket, building a whole questionnaire
 * through a fixed sequence of phases.
 *
 * It is not the fast chat with a slower model. A session runs for minutes and dozens of turns, the
 * agent leads and the person answers, and the questions only exist at the very end. So this screen
 * shows which phase is running - long silences are normal and otherwise indistinguishable from a
 * hang - and, when a session stops early, offers to pick it up again: the agent saves its work at
 * each phase boundary and can resume from the last one it finished.
 */

/** Where to remember an interrupted session, so a reload can still offer to continue it. */
const RESUME_KEY = "tale.agent.studyId";

const readResumable = () => {
    try {
        return sessionStorage.getItem(RESUME_KEY) || null;
    } catch {
        // Private windows and blocked site data: resuming is a convenience, not a requirement.
        return null;
    }
};

const rememberResumable = (studyId) => {
    try {
        if (studyId) sessionStorage.setItem(RESUME_KEY, studyId);
        else sessionStorage.removeItem(RESUME_KEY);
    } catch {
        /* see readResumable */
    }
};

export const AgentChat = ({ onFinished }) => {
    const { t } = useTranslation();

    const [lines, setLines] = useState([]);
    const [pending, setPending] = useState([]); // lines since the last question: the menu, if any
    const [prompt, setPrompt] = useState(null); // null means the agent is working
    const [phase, setPhase] = useState(null);
    const [text, setText] = useState("");
    const [end, setEnd] = useState(null);
    const [resumable, setResumable] = useState(readResumable);
    const [run, setRun] = useState({ id: 0, studyId: undefined }); // a new id starts a session

    const session = useRef(null);
    const scroller = useRef(null);

    const start = useCallback(
        (studyId) => {
            setLines([]);
            setPending([]);
            setPrompt(null);
            setPhase(null);
            setEnd(null);
            setText("");
            session.current = openQuestionnaire({
                studyId,
                onLine: (line) => {
                    if (isTechnicalLine(line)) return; // model fallbacks are ours to worry about
                    const announced = parsePhase(line);
                    if (announced) {
                        setPhase(announced);
                        return; // the banner says it; the transcript need not repeat it
                    }
                    if (/^[\s=—-]*$/.test(line)) return; // separator rules
                    setLines((l) => [...l, { from: "agent", text: line }]);
                    setPending((p) => [...p, line]);
                },
                onAsk: (p) => setPrompt(p),
                onEnd: (message) => {
                    setEnd(message);
                    setPrompt(null);
                    if (message.study_id) {
                        const keep = message.type === "termine" ? null : message.study_id;
                        rememberResumable(keep);
                        setResumable(keep);
                    }
                    if (message.type === "termine") onFinished?.(message);
                },
            });
        },
        [onFinished]
    );

    useEffect(() => {
        start(run.studyId);
        return () => session.current?.close();
    }, [run, start]);

    useEffect(() => {
        scroller.current?.scrollTo({ top: scroller.current.scrollHeight, behavior: "smooth" });
    }, [lines, prompt, end]);

    const send = (value) => {
        session.current.answer(value);
        setLines((l) => [...l, { from: "user", text: value || t("surveyBuilder.agent.defaultAnswer") }]);
        setPending([]);
        setPrompt(null);
        setText("");
    };

    const restart = (studyId) => {
        session.current?.close();
        setRun((previous) => ({ id: previous.id + 1, studyId }));
    };

    const options = useMemo(() => (prompt ? parseOptions(pending) : []), [prompt, pending]);
    const yesNo = prompt && /\(o\/n\)/.test(prompt + pending.join(" "));
    const allowsEmpty = prompt && /Entrée/.test(prompt);
    const working = !prompt && !end;

    return (
        <div className="flex flex-col gap-4 w-full">
            {!end && (
                <div className="w-full rounded-2xl border bg-gray-50 px-5 py-3 flex flex-col gap-2">
                    <div className="flex items-center gap-2 text-sm">
                        {working && <FaSpinner className="animate-spin text-[#00BDA9]" />}
                        <span className="font-semibold">
                            {phase
                                ? t("surveyBuilder.agent.phaseLabel", {
                                      number: phase.number,
                                      total: AGENT_LAST_PHASE,
                                  })
                                : t("surveyBuilder.agent.starting")}
                        </span>
                        {phase && <span className="text-gray-600">{phase.label}</span>}
                    </div>
                    <div className="h-1.5 w-full rounded-full bg-gray-200 overflow-hidden">
                        <div
                            className="h-full bg-tale-blue-light transition-all duration-500"
                            style={{
                                width: `${Math.min(100, ((phase?.number ?? 0) / AGENT_LAST_PHASE) * 100)}%`,
                            }}
                        />
                    </div>
                    <p className="text-xs text-gray-500">{t("surveyBuilder.agent.patience")}</p>
                </div>
            )}

            <div
                ref={scroller}
                className="w-full max-h-[440px] overflow-y-auto flex flex-col gap-2 px-1"
                style={{ scrollbarWidth: "none" }}
            >
                {lines.map((line, i) => (
                    <p
                        key={i}
                        className={
                            line.from === "user"
                                ? "self-end max-w-[80%] rounded-3xl !rounded-tr-none bg-[#00000010] px-4 py-2 text-sm"
                                : "self-start max-w-[90%] text-sm text-gray-700 whitespace-pre-line"
                        }
                    >
                        {line.text}
                    </p>
                ))}

                {end?.type === "interrompu" && (
                    <p className="text-sm text-orange-600">{t("surveyBuilder.agent.interrupted")}</p>
                )}
                {end?.type === "coupure" && (
                    <p className="text-sm text-red-600">{t("surveyBuilder.agent.connectionLost")}</p>
                )}
                {end?.type === "erreur" && (
                    <p className="text-sm text-red-600">
                        {end.texte}
                        {end.trace_id ? ` (${end.trace_id})` : ""}
                    </p>
                )}
                {end?.type === "termine" && (
                    <p className="text-sm text-[#00BDA9]">
                        {t("surveyBuilder.agent.finished", { count: end.nb_questions ?? 0 })}
                    </p>
                )}
            </div>

            {prompt && (
                <div className="flex flex-col gap-3">
                    {yesNo ? (
                        <div className="flex gap-3">
                            <TaleButton role="normal" className="!w-fit !px-6" onClick={() => send("o")}>
                                {t("surveyBuilder.agent.yes")}
                            </TaleButton>
                            <TaleButton
                                role="normal"
                                variant="outlined"
                                className="!w-fit !px-6"
                                onClick={() => send("n")}
                            >
                                {t("surveyBuilder.agent.no")}
                            </TaleButton>
                        </div>
                    ) : options.length > 0 ? (
                        <div className="flex flex-wrap gap-2">
                            {options.map((option) => (
                                <TaleButton
                                    key={option.key}
                                    role="normal"
                                    variant="outlined"
                                    className="!w-fit !px-4"
                                    onClick={() => send(option.key)}
                                >
                                    {option.key}. {option.label}
                                </TaleButton>
                            ))}
                        </div>
                    ) : (
                        <form
                            className="flex gap-2 items-end"
                            onSubmit={(e) => {
                                e.preventDefault();
                                if (text.trim() || allowsEmpty) send(text.trim());
                            }}
                        >
                            <TaleInput
                                className="grow"
                                value={text}
                                onChange={(e) => setText(e.target.value)}
                                placeholder={prompt}
                            />
                            <TaleButton role="normal" className="!w-fit !px-6" type="submit">
                                {t("surveyBuilder.agent.send")}
                            </TaleButton>
                            {allowsEmpty && (
                                <TaleButton
                                    role="normal"
                                    variant="outlined"
                                    className="!w-fit !px-6"
                                    type="button"
                                    onClick={() => send("")}
                                >
                                    {t("surveyBuilder.agent.keep")}
                                </TaleButton>
                            )}
                        </form>
                    )}
                </div>
            )}

            {end && end.type !== "termine" && (
                <div className="flex gap-3">
                    {/* Only an interrupted session has something to come back to: the agent names the
                        study it saved. A connection that drops mid-phase on a brand-new study does
                        not, since the id only arrives at the end. */}
                    {(end.study_id || resumable) && (
                        <TaleButton
                            role="normal"
                            className="!w-fit !px-6"
                            onClick={() => restart(end.study_id || resumable)}
                        >
                            {t("surveyBuilder.agent.continue")}
                        </TaleButton>
                    )}
                    <TaleButton
                        role="normal"
                        variant="outlined"
                        className="!w-fit !px-6"
                        onClick={() => {
                            rememberResumable(null);
                            setResumable(null);
                            restart(undefined);
                        }}
                    >
                        {t("surveyBuilder.agent.restart")}
                    </TaleButton>
                </div>
            )}
        </div>
    );
};

export default AgentChat;
