"use client";
import * as React from "react";
import { createAiAgentTransport } from "@open-mercato/ai-assistant/modules/ai_assistant/lib/agent-transport";
import { apiFetch } from "../backend/utils/api.js";
import {
  createAiServerConversation,
  importAiLocalConversation,
  loadAiServerTranscript,
  serverMessageToChatMessage
} from "./conversation-store.js";
function makeMessageId() {
  const random = Math.random().toString(36).slice(2, 10);
  const time = Date.now().toString(36);
  return `msg_${time}_${random}`;
}
function makeConversationId() {
  const g = globalThis;
  if (g.crypto && typeof g.crypto.randomUUID === "function") {
    try {
      return g.crypto.randomUUID();
    } catch {
    }
  }
  const rand = () => Math.random().toString(16).slice(2, 10);
  return `conv_${Date.now().toString(16)}_${rand()}${rand()}`;
}
const SESSION_STORAGE_PREFIX = "om-ai-chat:";
const SESSION_IMPORT_MARKER_PREFIX = "om-ai-chat-imported:";
const SESSION_STORAGE_VERSION = 1;
const SESSION_UPDATED_EVENT = "om-ai-chat-session-updated";
const SESSION_STREAM_STATE_EVENT = "om-ai-chat-session-stream-state";
const activeSessionStreams = /* @__PURE__ */ new Set();
function getSessionStorageKey(agent, conversationId) {
  if (typeof conversationId === "string" && conversationId.length > 0) {
    return `${SESSION_STORAGE_PREFIX}${agent}:${conversationId}`;
  }
  return `${SESSION_STORAGE_PREFIX}${agent}`;
}
function writeSessionStreamState(storageKey, active) {
  if (active) {
    activeSessionStreams.add(storageKey);
  } else {
    activeSessionStreams.delete(storageKey);
  }
  if (typeof window === "undefined") return;
  try {
    window.dispatchEvent(
      new CustomEvent(SESSION_STREAM_STATE_EVENT, {
        detail: { key: storageKey, active }
      })
    );
  } catch {
  }
}
function isSessionStreamActive(storageKey) {
  return activeSessionStreams.has(storageKey);
}
function readPersistedSession(agent, conversationId) {
  if (typeof window === "undefined") return null;
  try {
    const raw = window.localStorage.getItem(getSessionStorageKey(agent, conversationId));
    if (!raw) return null;
    const parsed = JSON.parse(raw);
    if (!parsed || parsed.v !== SESSION_STORAGE_VERSION) return null;
    if (typeof parsed.conversationId !== "string") return null;
    if (!Array.isArray(parsed.messages)) return null;
    const messages = parsed.messages.filter((entry) => {
      return !!entry && typeof entry === "object" && typeof entry.id === "string" && typeof entry.content === "string" && (entry.role === "user" || entry.role === "assistant");
    });
    return { v: SESSION_STORAGE_VERSION, conversationId: parsed.conversationId, messages };
  } catch {
    return null;
  }
}
function writePersistedSession(agent, session, conversationId, options) {
  if (typeof window === "undefined") return;
  try {
    const messages = session.messages.map((message) => {
      if (!message.files || message.files.length === 0) return message;
      const safeFiles = message.files.map(({ id, name, type, previewUrl }) => {
        const durable = typeof previewUrl === "string" && (previewUrl.startsWith("data:") || previewUrl.startsWith("/api/attachments/image/") || previewUrl.startsWith("/api/attachments/file/")) ? previewUrl : void 0;
        const base = id ? { id, name, type } : { name, type };
        return durable ? { ...base, previewUrl: durable } : base;
      });
      return { ...message, files: safeFiles };
    });
    const storageKey = getSessionStorageKey(agent, conversationId);
    window.localStorage.setItem(storageKey, JSON.stringify({ ...session, messages }));
    if (options?.notify) {
      window.dispatchEvent(
        new CustomEvent(SESSION_UPDATED_EVENT, {
          detail: { key: storageKey, agent, conversationId: session.conversationId }
        })
      );
    }
  } catch {
  }
}
function clearPersistedSession(agent, conversationId) {
  if (typeof window === "undefined") return;
  try {
    window.localStorage.removeItem(getSessionStorageKey(agent, conversationId));
  } catch {
  }
}
function getImportMarkerKey(agent, conversationId) {
  return `${SESSION_IMPORT_MARKER_PREFIX}${agent}:${conversationId}`;
}
function hasImportMarker(agent, conversationId) {
  if (typeof window === "undefined") return false;
  try {
    return window.localStorage.getItem(getImportMarkerKey(agent, conversationId)) === "1";
  } catch {
    return false;
  }
}
function writeImportMarker(agent, conversationId) {
  if (typeof window === "undefined") return;
  try {
    window.localStorage.setItem(getImportMarkerKey(agent, conversationId), "1");
  } catch {
  }
}
function getTransportEndpoint(agent, apiPath, providerOverride, modelOverride) {
  const transport = createAiAgentTransport({ agentId: agent, endpoint: apiPath });
  void transport;
  const base = apiPath && apiPath.length > 0 ? apiPath : "/api/ai_assistant/ai/chat";
  const separator = base.includes("?") ? "&" : "?";
  let url = `${base}${separator}agent=${encodeURIComponent(agent)}`;
  if (providerOverride) {
    url += `&provider=${encodeURIComponent(providerOverride)}`;
  }
  if (modelOverride) {
    url += `&model=${encodeURIComponent(modelOverride)}`;
  }
  return url;
}
function createBuilder() {
  return {
    text: "",
    reasoning: "",
    reasoningStreaming: false,
    toolCalls: [],
    toolCallCaptions: {},
    internalToolCallIds: [],
    uiParts: [],
    taskPlan: []
  };
}
const VALID_TASK_STATES = /* @__PURE__ */ new Set([
  "pending",
  "running",
  "done",
  "failed",
  "skipped"
]);
const VALID_TASK_SOURCES = /* @__PURE__ */ new Set(["runtime", "agent"]);
function coerceTaskSnapshot(raw) {
  if (!raw || typeof raw !== "object") return null;
  const value = raw;
  const id = typeof value.id === "string" ? value.id.trim() : "";
  if (!id) return null;
  const label = typeof value.label === "string" ? value.label : "";
  if (!label) return null;
  const state = VALID_TASK_STATES.has(value.state) ? value.state : "pending";
  const source = VALID_TASK_SOURCES.has(value.source) ? value.source : "runtime";
  const detail = typeof value.detail === "string" && value.detail.length > 0 ? value.detail : void 0;
  const toolCallId = typeof value.toolCallId === "string" && value.toolCallId.length > 0 ? value.toolCallId : void 0;
  return { id, label, state, source, detail, toolCallId };
}
const TERMINAL_TASK_STATES = /* @__PURE__ */ new Set([
  "done",
  "failed",
  "skipped"
]);
function mergeTaskSnapshot(current, incoming) {
  const idx = current.findIndex((task) => task.id === incoming.id);
  if (idx === -1) return [...current, incoming];
  const prior = current[idx];
  const nextState = TERMINAL_TASK_STATES.has(prior.state) ? prior.state : incoming.state;
  const merged = {
    ...prior,
    ...incoming,
    state: nextState
  };
  const next = current.slice();
  next[idx] = merged;
  return next;
}
function extractUiPartsFromOutput(output, toolCallId) {
  let parsed = output;
  if (typeof output === "string") {
    const trimmed = output.trim();
    if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return [];
    try {
      parsed = JSON.parse(trimmed);
    } catch {
      return [];
    }
  }
  if (!parsed || typeof parsed !== "object") return [];
  const value = parsed;
  const parts = [];
  if (value.status === "pending-confirmation" || value.status === "awaiting-confirmation") {
    const pendingActionId = typeof value.pendingActionId === "string" && value.pendingActionId.length > 0 ? value.pendingActionId : null;
    if (pendingActionId) {
      const agentId = typeof value.agentId === "string" ? value.agentId : typeof value.agent === "string" ? value.agent : void 0;
      parts.push({
        componentId: "mutation-preview-card",
        pendingActionId,
        payload: {
          pendingActionId,
          expiresAt: typeof value.expiresAt === "string" ? value.expiresAt : void 0,
          agentId,
          toolName: typeof value.toolName === "string" ? value.toolName : void 0
        },
        key: `${toolCallId}:mutation-preview-card`
      });
    }
  }
  if (value.uiPart && typeof value.uiPart === "object") {
    const part = value.uiPart;
    if (typeof part.componentId === "string" && part.componentId.length > 0) {
      parts.push({
        componentId: part.componentId,
        payload: part.payload,
        pendingActionId: typeof part.pendingActionId === "string" ? part.pendingActionId : void 0,
        key: `${toolCallId}:${part.componentId}`
      });
    }
  }
  if (Array.isArray(value.uiParts)) {
    value.uiParts.forEach((entry, index) => {
      if (!entry || typeof entry !== "object") return;
      const part = entry;
      if (typeof part.componentId !== "string" || part.componentId.length === 0) return;
      parts.push({
        componentId: part.componentId,
        payload: part.payload,
        pendingActionId: typeof part.pendingActionId === "string" ? part.pendingActionId : void 0,
        key: `${toolCallId}:${index}:${part.componentId}`
      });
    });
  }
  return parts;
}
function updateToolCall(state, id, patch) {
  if (!id) return state;
  const idx = state.toolCalls.findIndex((entry) => entry.id === id);
  if (idx === -1) {
    const next = {
      id,
      toolName: patch.toolName ?? "tool",
      caption: patch.caption ?? state.toolCallCaptions[id],
      state: patch.state ?? "pending",
      input: patch.input,
      output: patch.output,
      errorMessage: patch.errorMessage
    };
    return { ...state, toolCalls: [...state.toolCalls, next] };
  }
  const current = state.toolCalls[idx];
  const merged = {
    ...current,
    toolName: patch.toolName ?? current.toolName,
    caption: patch.caption ?? current.caption,
    state: patch.state ?? current.state,
    input: patch.input !== void 0 ? patch.input : current.input,
    output: patch.output !== void 0 ? patch.output : current.output,
    errorMessage: patch.errorMessage ?? current.errorMessage
  };
  const nextCalls = state.toolCalls.slice();
  nextCalls[idx] = merged;
  return { ...state, toolCalls: nextCalls };
}
function updateToolCallCaption(state, toolCallId, caption) {
  if (!toolCallId || !caption) return state;
  const toolCallCaptions = { ...state.toolCallCaptions, [toolCallId]: caption };
  const idx = state.toolCalls.findIndex((entry) => entry.id === toolCallId);
  if (idx === -1) return { ...state, toolCallCaptions };
  const nextCalls = state.toolCalls.slice();
  nextCalls[idx] = { ...nextCalls[idx], caption };
  return { ...state, toolCallCaptions, toolCalls: nextCalls };
}
function displayToolName(toolName) {
  if (typeof toolName !== "string") return void 0;
  return toolName.replace(/__/g, ".");
}
function isInternalTaskPlanTool(toolName) {
  return displayToolName(toolName) === "meta.update_task_plan";
}
function markInternalToolCall(state, toolCallId) {
  const id = String(toolCallId ?? "");
  if (!id || state.internalToolCallIds.includes(id)) return state;
  return { ...state, internalToolCallIds: [...state.internalToolCallIds, id] };
}
function isInternalToolCallId(state, toolCallId) {
  const id = String(toolCallId ?? "");
  return id.length > 0 && state.internalToolCallIds.includes(id);
}
function applyChunk(state, chunk) {
  switch (chunk.type) {
    case "text-delta":
      return {
        ...state,
        text: state.text + (typeof chunk.delta === "string" ? chunk.delta : "")
      };
    case "reasoning-start":
      return { ...state, reasoningStreaming: true };
    case "reasoning-delta":
      return {
        ...state,
        reasoning: state.reasoning + (typeof chunk.delta === "string" ? chunk.delta : ""),
        reasoningStreaming: true
      };
    case "reasoning-end":
      return { ...state, reasoningStreaming: false };
    case "tool-input-start":
      if (isInternalTaskPlanTool(chunk.toolName)) return markInternalToolCall(state, chunk.toolCallId);
      return updateToolCall(state, String(chunk.toolCallId ?? ""), {
        toolName: displayToolName(chunk.toolName),
        state: "pending"
      });
    case "tool-input-available":
      if (isInternalTaskPlanTool(chunk.toolName)) return markInternalToolCall(state, chunk.toolCallId);
      return updateToolCall(state, String(chunk.toolCallId ?? ""), {
        toolName: displayToolName(chunk.toolName),
        input: chunk.input,
        state: "pending"
      });
    case "tool-output-available": {
      const toolCallId = String(chunk.toolCallId ?? "");
      if (isInternalToolCallId(state, toolCallId)) return state;
      const next = updateToolCall(state, toolCallId, {
        output: chunk.output,
        state: "complete"
      });
      const newParts = extractUiPartsFromOutput(chunk.output, toolCallId);
      if (newParts.length === 0) return next;
      const seen = new Set(next.uiParts.map((entry) => entry.key));
      const merged = [...next.uiParts];
      for (const part of newParts) {
        if (seen.has(part.key)) continue;
        seen.add(part.key);
        merged.push(part);
      }
      if (merged.length === next.uiParts.length) return next;
      return { ...next, uiParts: merged };
    }
    case "tool-output-error":
      if (isInternalToolCallId(state, chunk.toolCallId)) return state;
      return updateToolCall(state, String(chunk.toolCallId ?? ""), {
        state: "error",
        errorMessage: typeof chunk.errorText === "string" ? chunk.errorText : "Tool error"
      });
    case "tool-input-error":
      if (isInternalTaskPlanTool(chunk.toolName)) return markInternalToolCall(state, chunk.toolCallId);
      return updateToolCall(state, String(chunk.toolCallId ?? ""), {
        toolName: displayToolName(chunk.toolName),
        input: chunk.input,
        state: "error",
        errorMessage: typeof chunk.errorText === "string" ? chunk.errorText : "Tool error"
      });
    case "data-agent-task-plan": {
      const rawTasks = Array.isArray(chunk.tasks) ? chunk.tasks : [];
      const coerced = rawTasks.map(coerceTaskSnapshot).filter((task) => task !== null);
      if (coerced.length === 0) return state;
      return coerced.reduce(
        (nextState, task) => updateToolCallCaption(nextState, task.toolCallId, task.label),
        { ...state, taskPlan: coerced }
      );
    }
    case "data-agent-task-update": {
      const incoming = coerceTaskSnapshot(chunk.task);
      if (!incoming) return state;
      return updateToolCallCaption(
        { ...state, taskPlan: mergeTaskSnapshot(state.taskPlan, incoming) },
        incoming.toolCallId,
        incoming.label
      );
    }
    default:
      return state;
  }
}
function mergeAssistantMessage(current, state) {
  return {
    ...current,
    content: state.text,
    reasoning: state.reasoning ? state.reasoning : void 0,
    reasoningStreaming: state.reasoning ? state.reasoningStreaming : void 0,
    toolCalls: state.toolCalls.length > 0 ? state.toolCalls : void 0,
    uiParts: state.uiParts.length > 0 ? state.uiParts : void 0,
    taskPlan: state.taskPlan.length > 0 ? state.taskPlan : void 0
  };
}
function parseSseLines(buffer) {
  const events = [];
  let rest = buffer;
  for (; ; ) {
    const idx = rest.indexOf("\n\n");
    if (idx === -1) break;
    events.push(rest.slice(0, idx));
    rest = rest.slice(idx + 2);
  }
  return { events, rest };
}
function extractDataPayload(eventBlock) {
  const lines = eventBlock.split("\n");
  const dataLines = [];
  for (const line of lines) {
    if (line.startsWith("data: ")) {
      dataLines.push(line.slice(6));
    } else if (line.startsWith("data:")) {
      dataLines.push(line.slice(5));
    }
  }
  if (dataLines.length === 0) return null;
  return dataLines.join("\n");
}
async function readErrorEnvelope(response) {
  try {
    const data = await response.clone().json();
    if (data && typeof data === "object") {
      const rawMessage = typeof data.error === "string" && data.error || typeof data.message === "string" && data.message || "";
      const rawCode = typeof data.code === "string" ? data.code : void 0;
      if (rawMessage || rawCode) {
        return {
          code: rawCode,
          message: rawMessage || "Agent dispatch failed."
        };
      }
    }
  } catch {
  }
  const text = await response.text().catch(() => "");
  return { message: text || `Agent dispatch failed (${response.status}).` };
}
function useAiChat(input) {
  const { agent, apiPath, pageContext, attachmentIds, debug, initialMessages, onError, conversationId: conversationIdInput, providerOverride, modelOverride } = input;
  const persistedRef = React.useRef("unread");
  if (persistedRef.current === "unread") {
    persistedRef.current = typeof conversationIdInput === "string" && conversationIdInput.length > 0 ? readPersistedSession(agent, conversationIdInput) : readPersistedSession(agent);
  }
  const persisted = persistedRef.current;
  const mintedConversationIdRef = React.useRef(null);
  if (mintedConversationIdRef.current === null) {
    mintedConversationIdRef.current = persisted?.conversationId ?? makeConversationId();
  }
  const effectiveConversationId = typeof conversationIdInput === "string" && conversationIdInput.length > 0 ? conversationIdInput : mintedConversationIdRef.current;
  const persistKey = typeof conversationIdInput === "string" && conversationIdInput.length > 0 ? conversationIdInput : null;
  const sessionStorageKey = getSessionStorageKey(agent, persistKey);
  const [messages, setMessages] = React.useState(() => {
    if (persisted && persisted.messages.length > 0) {
      return persisted.messages;
    }
    return (initialMessages ?? []).map((entry) => ({
      id: makeMessageId(),
      role: entry.role,
      content: entry.content
    }));
  });
  const [status, setStatusInternal] = React.useState(
    () => isSessionStreamActive(sessionStorageKey) ? "streaming" : "idle"
  );
  const latestMessagesRef = React.useRef(messages);
  const latestStatusRef = React.useRef(status);
  const persistKeyRef = React.useRef(null);
  const agentRef = React.useRef(agent);
  const pageContextRef = React.useRef(pageContext);
  const effectiveConversationIdRef = React.useRef(effectiveConversationId);
  const sessionStorageKeyRef = React.useRef(sessionStorageKey);
  const mountedRef = React.useRef(true);
  React.useEffect(() => {
    latestMessagesRef.current = messages;
  }, [messages]);
  React.useEffect(() => {
    latestStatusRef.current = status;
  }, [status]);
  React.useEffect(() => {
    persistKeyRef.current = typeof conversationIdInput === "string" && conversationIdInput.length > 0 ? conversationIdInput : null;
  }, [conversationIdInput]);
  React.useEffect(() => {
    agentRef.current = agent;
  }, [agent]);
  React.useEffect(() => {
    pageContextRef.current = pageContext;
  }, [pageContext]);
  React.useEffect(() => {
    effectiveConversationIdRef.current = effectiveConversationId;
  }, [effectiveConversationId]);
  React.useEffect(() => {
    sessionStorageKeyRef.current = sessionStorageKey;
  }, [sessionStorageKey]);
  React.useEffect(() => {
    if (status !== "idle") return;
    if (messages.length === 0) {
      clearPersistedSession(agent, persistKey);
      return;
    }
    writePersistedSession(
      agent,
      {
        v: SESSION_STORAGE_VERSION,
        conversationId: effectiveConversationId,
        messages
      },
      persistKey
    );
  }, [agent, effectiveConversationId, messages, persistKey, status]);
  const persistSnapshot = React.useCallback((snapshot, notify = false) => {
    if (snapshot.length === 0) return;
    writePersistedSession(
      agentRef.current,
      {
        v: SESSION_STORAGE_VERSION,
        conversationId: effectiveConversationIdRef.current,
        messages: snapshot
      },
      persistKeyRef.current,
      { notify }
    );
  }, []);
  const updateMessages = React.useCallback(
    (updater, options) => {
      const current = latestMessagesRef.current;
      const next = typeof updater === "function" ? updater(current) : updater;
      latestMessagesRef.current = next;
      if (mountedRef.current) {
        setMessages(next);
      } else if (options?.persistWhenUnmounted) {
        persistSnapshot(next, true);
      }
    },
    [persistSnapshot]
  );
  React.useEffect(() => {
    let cancelled = false;
    const localCandidate = readPersistedSession(agent, persistKey);
    async function hydrateFromServer() {
      const transcript = await loadAiServerTranscript(effectiveConversationId, { limit: 100 });
      if (cancelled || latestStatusRef.current !== "idle") return;
      if (transcript) {
        const serverMessages = transcript.messages.map(serverMessageToChatMessage).filter((message) => message !== null);
        if (serverMessages.length > 0 || !localCandidate || localCandidate.messages.length === 0) {
          updateMessages(serverMessages);
          if (serverMessages.length > 0) persistSnapshot(serverMessages, true);
          return;
        }
      }
      if (!localCandidate || localCandidate.messages.length === 0) {
        if (!transcript) {
          void createAiServerConversation({
            agentId: agent,
            conversationId: effectiveConversationId,
            pageContext: pageContextRef.current ?? null
          });
        }
        return;
      }
      if (hasImportMarker(agent, effectiveConversationId)) return;
      const imported = await importAiLocalConversation({
        agentId: agent,
        conversationId: effectiveConversationId,
        pageContext: pageContextRef.current ?? null,
        messages: localCandidate.messages
      });
      if (cancelled || !imported) return;
      writeImportMarker(agent, effectiveConversationId);
    }
    void hydrateFromServer();
    return () => {
      cancelled = true;
    };
  }, [agent, effectiveConversationId, persistKey, persistSnapshot, updateMessages]);
  const setStatus = React.useCallback((next) => {
    if (mountedRef.current) {
      setStatusInternal(next);
    }
  }, []);
  const [error, setError] = React.useState(null);
  const [lastRequestDebug, setLastRequestDebug] = React.useState(null);
  const [lastResponseDebug, setLastResponseDebug] = React.useState(null);
  const [lastLoopTrace, setLastLoopTrace] = React.useState(null);
  const abortRef = React.useRef(null);
  const onErrorRef = React.useRef(onError);
  React.useEffect(() => {
    onErrorRef.current = onError;
  }, [onError]);
  const emitError = React.useCallback((envelope) => {
    if (mountedRef.current) {
      setError(envelope);
      try {
        onErrorRef.current?.(envelope);
      } catch {
      }
    }
  }, []);
  const cancel = React.useCallback(() => {
    if (abortRef.current) {
      abortRef.current.abort();
      abortRef.current = null;
    }
    writeSessionStreamState(sessionStorageKeyRef.current, false);
    setStatus("idle");
  }, [setStatus]);
  const reset = React.useCallback(() => {
    cancel();
    updateMessages([]);
    setError(null);
    setLastRequestDebug(null);
    setLastResponseDebug(null);
    setLastLoopTrace(null);
    clearPersistedSession(agent, persistKey);
    mintedConversationIdRef.current = makeConversationId();
  }, [agent, cancel, persistKey, updateMessages]);
  const sendMessage = React.useCallback(
    async (textInput, files) => {
      const trimmed = textInput.trim();
      if (!trimmed) return;
      if (abortRef.current) {
        abortRef.current.abort();
      }
      setError(null);
      const userMessage = {
        id: makeMessageId(),
        role: "user",
        content: trimmed,
        files: files && files.length > 0 ? files : void 0
      };
      const assistantMessage = {
        id: makeMessageId(),
        role: "assistant",
        content: ""
      };
      const assistantId = assistantMessage.id;
      const outgoingHistory = [...latestMessagesRef.current, userMessage];
      updateMessages([...outgoingHistory, assistantMessage], { persistWhenUnmounted: true });
      setStatus("submitting");
      writeSessionStreamState(sessionStorageKey, true);
      const controller = new AbortController();
      abortRef.current = controller;
      const url = getTransportEndpoint(agent, apiPath, providerOverride, modelOverride);
      const body = {
        messages: outgoingHistory.map((message) => ({
          id: message.id,
          role: message.role,
          content: message.content,
          files: message.files,
          uiParts: message.uiParts
        })),
        pageContext,
        attachmentIds,
        debug,
        conversationId: effectiveConversationId
      };
      if (mountedRef.current) {
        setLastRequestDebug({ url, body });
      }
      let response;
      try {
        response = await apiFetch(url, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            Accept: "text/event-stream, text/plain, application/json"
          },
          body: JSON.stringify(body),
          signal: controller.signal
        });
      } catch (requestError) {
        if (requestError?.name === "AbortError") {
          writeSessionStreamState(sessionStorageKey, false);
          setStatus("idle");
          abortRef.current = null;
          return;
        }
        const message = requestError instanceof Error ? requestError.message : "Network request failed.";
        emitError({ message });
        writeSessionStreamState(sessionStorageKey, false);
        setStatus("idle");
        abortRef.current = null;
        return;
      }
      if (!response.ok) {
        const envelope = await readErrorEnvelope(response);
        if (mountedRef.current) {
          setLastResponseDebug({ status: response.status, text: envelope.message });
        }
        emitError(envelope);
        writeSessionStreamState(sessionStorageKey, false);
        setStatus("idle");
        updateMessages((current) => current.filter((entry) => entry.id !== assistantId), {
          persistWhenUnmounted: true
        });
        abortRef.current = null;
        return;
      }
      const bodyStream = response.body;
      if (!bodyStream) {
        if (mountedRef.current) {
          setLastResponseDebug({ status: response.status, text: "" });
        }
        writeSessionStreamState(sessionStorageKey, false);
        setStatus("idle");
        abortRef.current = null;
        return;
      }
      let pendingLoopTrace = null;
      const headerGet = (name) => {
        const headers = response.headers;
        if (!headers || typeof headers.get !== "function") return null;
        try {
          return headers.get(name);
        } catch {
          return null;
        }
      };
      const isUiMessageStream = headerGet("x-vercel-ai-ui-message-stream") !== null || (headerGet("content-type") ?? "").includes("event-stream");
      setStatus("streaming");
      const reader = bodyStream.getReader();
      const decoder = new TextDecoder();
      let streamedRaw = "";
      let builder = createBuilder();
      let sseBuffer = "";
      const flushUiMessageBuffer = (extra) => {
        if (extra) sseBuffer += extra;
        const { events, rest } = parseSseLines(sseBuffer);
        sseBuffer = rest;
        for (const block of events) {
          const data = extractDataPayload(block);
          if (!data) continue;
          if (data === "[DONE]") continue;
          try {
            const parsed = JSON.parse(data);
            if (!parsed || typeof parsed.type !== "string") continue;
            if (parsed.type === "loop-finish") {
              pendingLoopTrace = parsed.trace ?? null;
            } else {
              builder = applyChunk(builder, parsed);
            }
          } catch {
          }
        }
      };
      try {
        while (true) {
          const { value, done } = await reader.read();
          if (done) break;
          if (!value) continue;
          const piece = decoder.decode(value, { stream: true });
          if (!piece) continue;
          streamedRaw += piece;
          if (isUiMessageStream) {
            flushUiMessageBuffer(piece);
          } else {
            builder = { ...builder, text: streamedRaw };
          }
          const snapshotBuilder = builder;
          updateMessages(
            (current) => current.map(
              (entry) => entry.id === assistantId ? mergeAssistantMessage(entry, snapshotBuilder) : entry
            ),
            { persistWhenUnmounted: true }
          );
        }
        const tail = decoder.decode();
        if (tail) {
          streamedRaw += tail;
          if (isUiMessageStream) {
            flushUiMessageBuffer(tail);
          } else {
            builder = { ...builder, text: streamedRaw };
          }
        }
        if (isUiMessageStream && sseBuffer.length > 0) {
          flushUiMessageBuffer("\n\n");
        }
        builder = { ...builder, reasoningStreaming: false };
        const finalSnapshot = builder;
        updateMessages(
          (current) => current.map(
            (entry) => entry.id === assistantId ? mergeAssistantMessage(entry, finalSnapshot) : entry
          ),
          { persistWhenUnmounted: true }
        );
        if (mountedRef.current) {
          setLastResponseDebug({ status: response.status, text: streamedRaw });
          if (pendingLoopTrace !== null) {
            setLastLoopTrace(pendingLoopTrace);
          }
        }
        const isEmpty = !builder.text.trim() && builder.toolCalls.length === 0 && !builder.reasoning;
        if (isEmpty) {
          emitError({
            code: "empty_response",
            message: "The AI agent returned an empty response. This usually means the LLM provider rejected the request (invalid API key, rate limit, or model error). Check your server logs for details."
          });
          updateMessages((current) => current.filter((entry) => entry.id !== assistantId), {
            persistWhenUnmounted: true
          });
        }
      } catch (streamError) {
        if (streamError?.name === "AbortError") {
        } else {
          const rawMessage = streamError instanceof Error ? streamError.message : "Stream interrupted.";
          const message = rawMessage.includes("API") ? rawMessage : `${rawMessage} \u2014 check server logs for LLM provider details.`;
          emitError({ code: "stream_error", message });
          updateMessages((current) => current.filter((entry) => entry.id !== assistantId), {
            persistWhenUnmounted: true
          });
        }
      } finally {
        reader.releaseLock();
        if (abortRef.current === controller) {
          abortRef.current = null;
        }
        writeSessionStreamState(sessionStorageKey, false);
        setStatus("idle");
      }
    },
    [agent, apiPath, attachmentIds, debug, effectiveConversationId, emitError, messages, modelOverride, pageContext, providerOverride]
  );
  React.useEffect(() => {
    if (typeof window === "undefined") return;
    const handleSessionUpdate = (event) => {
      const detail = event.detail;
      if (!detail || detail.key !== sessionStorageKey) return;
      const next = readPersistedSession(agent, persistKey);
      if (!next) return;
      if (next.conversationId !== effectiveConversationId) return;
      updateMessages(next.messages);
    };
    window.addEventListener(SESSION_UPDATED_EVENT, handleSessionUpdate);
    return () => {
      window.removeEventListener(SESSION_UPDATED_EVENT, handleSessionUpdate);
    };
  }, [agent, effectiveConversationId, persistKey, sessionStorageKey, updateMessages]);
  React.useEffect(() => {
    if (isSessionStreamActive(sessionStorageKey)) {
      setStatusInternal("streaming");
    }
    if (typeof window === "undefined") return;
    const handleStreamState = (event) => {
      const detail = event.detail;
      if (!detail || detail.key !== sessionStorageKey) return;
      setStatusInternal(detail.active ? "streaming" : "idle");
    };
    window.addEventListener(SESSION_STREAM_STATE_EVENT, handleStreamState);
    return () => {
      window.removeEventListener(SESSION_STREAM_STATE_EVENT, handleStreamState);
    };
  }, [sessionStorageKey]);
  React.useEffect(() => {
    mountedRef.current = true;
    return () => {
      const finalMessages = latestMessagesRef.current;
      if (finalMessages.length > 0) {
        persistSnapshot(finalMessages, true);
      }
      mountedRef.current = false;
    };
  }, [persistSnapshot]);
  return {
    messages,
    status,
    error,
    lastRequestDebug,
    lastResponseDebug,
    conversationId: effectiveConversationId,
    lastLoopTrace,
    sendMessage,
    cancel,
    reset
  };
}
export {
  useAiChat
};
//# sourceMappingURL=useAiChat.js.map
