import { listen } from "@tauri-apps/api/event";
import { FormEvent, useEffect, useMemo, useState } from "react";
import { Link, useParams } from "react-router-dom";
import {
  createLiveSession,
  listAgents,
  listLiveMessages,
  listProjectModules,
  listLiveSessions,
  setLiveSessionArchived,
  sendLiveMessage,
} from "../../lib/api";
import { getErrorMessage } from "../../lib/errors";
import type { Agent, LiveMessage, LiveSession } from "../../lib/types";
import {
  backLinkClass,
  buttonClass,
  cardContentClass,
  inputClass,
  noticeClass,
  pageStackClass,
  pageTitleClass,
  pillClass,
} from "../ui/primitives";

interface LiveEventPayload {
  project_id: string;
  session_id: string;
  message: LiveMessage;
}

interface LiveStreamChunkPayload {
  project_id: string;
  session_id: string;
  chunk: string;
}

interface LiveStreamStatusPayload {
  project_id: string;
  session_id: string;
  error?: string | null;
}

function StreamingAgentBubble({ content }: { content: string }) {
  return (
    <div className="rounded bg-blue-100 px-3 py-2 text-sm text-blue-900">
      <div className="mb-1 text-[11px] font-semibold uppercase tracking-wide opacity-70">
        agent
      </div>
      <div className="whitespace-pre-wrap">{content || "Réponse en cours..."}</div>
      <div
        role="status"
        aria-live="polite"
        className="mt-3 flex items-center gap-2 text-[11px] font-semibold uppercase tracking-wide opacity-70"
      >
        <span aria-hidden="true" className="flex items-center gap-1">
          <span
            className="h-1.5 w-1.5 animate-bounce rounded-full bg-current"
            style={{ animationDelay: "-0.3s" }}
          />
          <span
            className="h-1.5 w-1.5 animate-bounce rounded-full bg-current"
            style={{ animationDelay: "-0.15s" }}
          />
          <span className="h-1.5 w-1.5 animate-bounce rounded-full bg-current" />
        </span>
        <span>Génération en cours</span>
      </div>
    </div>
  );
}

export default function ProjectLiveAgent() {
  const { projectId } = useParams<{ projectId: string }>();
  const [agents, setAgents] = useState<Agent[]>([]);
  const [sessions, setSessions] = useState<LiveSession[]>([]);
  const [messages, setMessages] = useState<LiveMessage[]>([]);
  const [selectedSessionId, setSelectedSessionId] = useState<string>("");
  const [selectedAgentId, setSelectedAgentId] = useState<string>("");
  const [sessionTitle, setSessionTitle] = useState("");
  const [draft, setDraft] = useState("");
  const [sending, setSending] = useState(false);
  const [creatingSession, setCreatingSession] = useState(false);
  const [updatingSessionStatus, setUpdatingSessionStatus] = useState(false);
  const [moduleEnabled, setModuleEnabled] = useState(true);
  const [streamingAgentResponse, setStreamingAgentResponse] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);

  const usableAgents = useMemo(
    () => agents.filter((agent) => agent.role === "analyst" || agent.role === "developer"),
    [agents]
  );
  const selectedSession = useMemo(
    () => sessions.find((session) => session.id === selectedSessionId) ?? null,
    [sessions, selectedSessionId]
  );
  const activeSessions = useMemo(
    () => sessions.filter((session) => session.status !== "archived"),
    [sessions]
  );
  const archivedSessions = useMemo(
    () => sessions.filter((session) => session.status === "archived"),
    [sessions]
  );

  async function refreshSessions(defaultSessionId?: string) {
    if (!projectId) return;

    const result = await listLiveSessions(projectId);
    setSessions(result);

    const targetSessionId = defaultSessionId ?? selectedSessionId;
    const firstSessionId = result[0]?.id;

    if (targetSessionId && result.some((session) => session.id === targetSessionId)) {
      setSelectedSessionId(targetSessionId);
      const sessionMessages = await listLiveMessages(targetSessionId);
      setMessages(sessionMessages);
      return;
    }

    if (firstSessionId) {
      setSelectedSessionId(firstSessionId);
      const sessionMessages = await listLiveMessages(firstSessionId);
      setMessages(sessionMessages);
      return;
    }

    setSelectedSessionId("");
    setMessages([]);
  }

  useEffect(() => {
    async function load() {
      if (!projectId) return;

      setError(null);
      try {
        const [availableAgents, modules] = await Promise.all([
          listAgents(),
          listProjectModules(projectId),
        ]);
        setAgents(availableAgents);
        setModuleEnabled(
          modules.find((mod) => mod.module_key === "ai_live_chat")?.enabled ?? true
        );

        const firstAgentId = availableAgents[0]?.id ?? "";
        setSelectedAgentId((current) => current || firstAgentId);

        await refreshSessions();
      } catch (err: unknown) {
        setError(getErrorMessage(err));
      }
    }

    void load();
  }, [projectId]);

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

    let cancelled = false;
    let stop: (() => void) | null = null;

    const setup = async () => {
      try {
        const [unlistenMessage, unlistenStarted, unlistenChunk, unlistenFinished, unlistenError] =
          await Promise.all([
            listen<LiveEventPayload>("live-agent-message", (event) => {
              const payload = event.payload;
              if (payload.project_id !== projectId) return;
              if (payload.session_id !== selectedSessionId) return;

              setMessages((prev) => {
                const existingIndex = prev.findIndex((msg) => msg.id === payload.message.id);
                if (existingIndex === -1) {
                  return [...prev, payload.message];
                }

                const next = [...prev];
                next[existingIndex] = payload.message;
                return next;
              });

              if (payload.message.sender === "agent" && payload.message.content.trim() !== "") {
                setStreamingAgentResponse(null);
              }
            }),
            listen<LiveStreamStatusPayload>("live-agent-stream-started", (event) => {
              const payload = event.payload;
              if (payload.project_id !== projectId) return;
              if (payload.session_id !== selectedSessionId) return;
              setStreamingAgentResponse("");
            }),
            listen<LiveStreamChunkPayload>("live-agent-stream-chunk", (event) => {
              const payload = event.payload;
              if (payload.project_id !== projectId) return;
              if (payload.session_id !== selectedSessionId) return;
              setStreamingAgentResponse((prev) => `${prev ?? ""}${payload.chunk}`);
            }),
            listen<LiveStreamStatusPayload>("live-agent-stream-finished", (event) => {
              const payload = event.payload;
              if (payload.project_id !== projectId) return;
              if (payload.session_id !== selectedSessionId) return;
              setStreamingAgentResponse(null);
            }),
            listen<LiveStreamStatusPayload>("live-agent-stream-error", (event) => {
              const payload = event.payload;
              if (payload.project_id !== projectId) return;
              if (payload.session_id !== selectedSessionId) return;
              setStreamingAgentResponse(null);
              setMessages((prev) =>
                prev.filter((msg) => !(msg.sender === "agent" && msg.content.trim() === ""))
              );
              if (payload.error) {
                setError(payload.error);
              }
            }),
          ]);

        const cleanup = () => {
          unlistenMessage();
          unlistenStarted();
          unlistenChunk();
          unlistenFinished();
          unlistenError();
        };

        if (cancelled) {
          cleanup();
          return;
        }

        stop = cleanup;
      } catch (err: unknown) {
        if (!cancelled) {
          setError(getErrorMessage(err));
        }
      }
    };

    void setup();

    return () => {
      cancelled = true;
      if (stop) {
        stop();
      }
    };
  }, [projectId, selectedSessionId]);

  async function handleCreateSession(event: FormEvent) {
    event.preventDefault();
    if (!projectId || !selectedAgentId) return;
    if (!moduleEnabled) {
      setError("Le module Live chat agent est désactivé pour ce projet.");
      return;
    }

    setCreatingSession(true);
    setError(null);
    try {
      const created = await createLiveSession(projectId, selectedAgentId, sessionTitle);
      setSessionTitle("");
      await refreshSessions(created.id);
    } catch (err: unknown) {
      setError(getErrorMessage(err));
    } finally {
      setCreatingSession(false);
    }
  }

  async function handleSendMessage(event: FormEvent) {
    event.preventDefault();
    if (!selectedSessionId || !draft.trim()) return;
    if (!moduleEnabled) {
      setError("Le module Live chat agent est désactivé pour ce projet.");
      return;
    }
    if (selectedSession?.status === "archived") {
      setError("Cette discussion live est archivée et ne peut plus recevoir de message.");
      return;
    }

    const content = draft.trim();
    setDraft("");
    setSending(true);
    setError(null);

    try {
      await sendLiveMessage(selectedSessionId, content);
    } catch (err: unknown) {
      setDraft(content);
      setStreamingAgentResponse(null);
      setError(getErrorMessage(err));
    } finally {
      setSending(false);
    }
  }

  async function handleToggleArchive(archived: boolean) {
    if (!selectedSession) return;

    setUpdatingSessionStatus(true);
    setError(null);
    setStreamingAgentResponse(null);
    try {
      await setLiveSessionArchived(selectedSession.id, archived);
      await refreshSessions(selectedSession.id);
    } catch (err: unknown) {
      setError(getErrorMessage(err));
    } finally {
      setUpdatingSessionStatus(false);
    }
  }

  async function handleSessionChange(sessionId: string) {
    setSelectedSessionId(sessionId);
    setStreamingAgentResponse(null);
    if (!sessionId) {
      setMessages([]);
      return;
    }

    try {
      const result = await listLiveMessages(sessionId);
      setMessages(result);
    } catch (err: unknown) {
      setError(getErrorMessage(err));
    }
  }

  return (
    <div className={pageStackClass}>
      <div>
        {projectId && (
          <Link to={`/projects/${projectId}`} className={backLinkClass}>
            Back
          </Link>
        )}
        <h2 className={pageTitleClass}>Live agent</h2>
      </div>

      {error && (
        <div className={noticeClass("error", true)}>
          {error}
        </div>
      )}
      {!moduleEnabled && (
        <div className={noticeClass("warning", true)}>
          Le module est désactivé. La lecture reste possible, mais la création de session et l'envoi de message sont bloqués.
        </div>
      )}

      <form onSubmit={handleCreateSession} className={cardContentClass}>
        <h3 className="mb-3 text-sm font-semibold text-gray-800">Nouvelle session</h3>
        <div className="grid gap-3 md:grid-cols-3">
          <select
            value={selectedAgentId}
            onChange={(e) => setSelectedAgentId(e.target.value)}
            className={inputClass}
          >
            {usableAgents.map((agent) => (
              <option key={agent.id} value={agent.id}>
                {agent.name} ({agent.tool})
              </option>
            ))}
          </select>
          <input
            type="text"
            value={sessionTitle}
            onChange={(e) => setSessionTitle(e.target.value)}
            placeholder="Titre de session (optionnel)"
            className={inputClass}
          />
          <button
            type="submit"
            disabled={creatingSession || !selectedAgentId || !moduleEnabled}
            className={buttonClass({ variant: "primary" })}
          >
            {creatingSession ? "Création..." : "Créer la session"}
          </button>
        </div>
      </form>

      <div className="grid gap-4 md:grid-cols-[260px,1fr]">
        <div className={`${cardContentClass} p-3`}>
          <div className="mb-2 text-xs font-semibold uppercase tracking-wide text-gray-500">
            Sessions
          </div>
          <div className="space-y-4">
            <div>
              <div className="mb-2 text-[11px] font-semibold uppercase tracking-wide text-gray-400">
                Actives
              </div>
              <div className="space-y-2">
                {activeSessions.map((session) => (
                  <button
                    type="button"
                    key={session.id}
                    onClick={() => void handleSessionChange(session.id)}
                    className={`${pillClass(selectedSessionId === session.id)} w-full text-left`}
                  >
                    <div className="truncate">{session.title}</div>
                    <div className="mt-1 text-[11px] opacity-70">
                      {new Date(session.updated_at).toLocaleString()}
                    </div>
                  </button>
                ))}
                {activeSessions.length === 0 && sessions.length > 0 && (
                  <div className="text-xs text-gray-400">Aucune session active.</div>
                )}
              </div>
            </div>

            <div>
              <div className="mb-2 text-[11px] font-semibold uppercase tracking-wide text-gray-400">
                Archivées
              </div>
              <div className="space-y-2">
                {archivedSessions.map((session) => (
                  <button
                    type="button"
                    key={session.id}
                    onClick={() => void handleSessionChange(session.id)}
                    className={`w-full rounded px-3 py-2 text-left text-sm ${
                      selectedSessionId === session.id
                        ? "bg-amber-100 text-amber-900"
                        : "bg-gray-100 text-gray-700 hover:bg-gray-200"
                    }`}
                  >
                    <div className="flex items-center justify-between gap-2">
                      <div className="truncate">{session.title}</div>
                      <span className="shrink-0 rounded-full bg-amber-200 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-amber-800">
                        Archivée
                      </span>
                    </div>
                    <div className="mt-1 text-[11px] opacity-70">
                      {new Date(session.updated_at).toLocaleString()}
                    </div>
                  </button>
                ))}
                {archivedSessions.length === 0 && sessions.length > 0 && (
                  <div className="text-xs text-gray-400">Aucune session archivée.</div>
                )}
              </div>
            </div>

            {sessions.length === 0 && <div className="text-xs text-gray-400">Aucune session.</div>}
          </div>
        </div>

        <div className={cardContentClass}>
          <div className="mb-3 flex items-center justify-between gap-3">
            <div>
              <div className="text-sm font-semibold text-gray-800">Discussion</div>
              {selectedSession && (
                <div className="mt-1 text-xs text-gray-500">
                  {selectedSession.title}
                  {selectedSession.status === "archived" && (
                    <span className="ml-2 rounded-full bg-amber-100 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-amber-800">
                      Archivée
                    </span>
                  )}
                </div>
              )}
            </div>
            {selectedSession && (
              <button
                type="button"
                onClick={() => void handleToggleArchive(selectedSession.status !== "archived")}
                disabled={updatingSessionStatus || sending || streamingAgentResponse !== null}
                className={`rounded px-3 py-1.5 text-xs font-medium disabled:opacity-50 ${
                  selectedSession.status === "archived"
                    ? "bg-emerald-100 text-emerald-700 hover:bg-emerald-200"
                    : "bg-amber-100 text-amber-800 hover:bg-amber-200"
                }`}
              >
                {updatingSessionStatus
                  ? "Mise à jour..."
                  : selectedSession.status === "archived"
                  ? "Restaurer"
                  : "Archiver"}
              </button>
            )}
          </div>
          {selectedSession?.status === "archived" && (
            <div className="mb-3 rounded border border-amber-200 bg-amber-50 p-2 text-sm text-amber-700">
              Cette discussion est archivée. Elle reste consultable, mais l&apos;envoi de nouveaux
              messages est désactivé.
            </div>
          )}
          <div className="max-h-[360px] space-y-2 overflow-y-auto rounded border border-gray-100 bg-gray-50 p-3">
            {messages
              .filter((msg) => !(msg.sender === "agent" && msg.content.trim() === ""))
              .map((msg) => (
              <div
                key={msg.id}
                className={`rounded px-3 py-2 text-sm ${
                  msg.sender === "agent"
                    ? "bg-blue-100 text-blue-900"
                    : msg.sender === "system"
                    ? "bg-amber-100 text-amber-900"
                    : "bg-gray-200 text-gray-800"
                }`}
              >
                <div className="mb-1 text-[11px] font-semibold uppercase tracking-wide opacity-70">
                  {msg.sender}
                </div>
                <div className="whitespace-pre-wrap">{msg.content}</div>
              </div>
            ))}
            {streamingAgentResponse !== null && (
              <StreamingAgentBubble content={streamingAgentResponse} />
            )}
            {messages.length === 0 && streamingAgentResponse === null && (
              <div className="text-sm text-gray-400">Pas encore de message.</div>
            )}
          </div>

          <form onSubmit={handleSendMessage} className="mt-3 flex gap-2">
            <input
              type="text"
              value={draft}
              onChange={(e) => setDraft(e.target.value)}
              placeholder={
                selectedSession?.status === "archived"
                  ? "Discussion archivée en lecture seule"
                  : "Ton message..."
              }
              disabled={!selectedSessionId || sending || selectedSession?.status === "archived"}
              className={`flex-1 ${inputClass}`}
            />
            <button
              type="submit"
              disabled={
                !selectedSessionId ||
                sending ||
                !draft.trim() ||
                !moduleEnabled ||
                selectedSession?.status === "archived"
              }
              className={buttonClass({ variant: "neutralDark" })}
            >
              {sending ? "Envoi..." : "Envoyer"}
            </button>
          </form>
        </div>
      </div>
    </div>
  );
}
