import {
  createOpenCodeClient
} from "./opencode-client.js";
let clientInstance = null;
function getClient() {
  if (!clientInstance) {
    clientInstance = createOpenCodeClient();
  }
  return clientInstance;
}
async function handleOpenCodeMessage(request) {
  const client = getClient();
  const { message, sessionId, model } = request;
  if (!message) {
    throw new Error("Message is required");
  }
  let session;
  if (sessionId) {
    session = await client.getSession(sessionId);
  } else {
    session = await client.createSession();
  }
  const result = await client.sendMessage(session.id, message, { model });
  return {
    sessionId: session.id,
    result
  };
}
async function handleOpenCodeHealth() {
  const client = getClient();
  const url = process.env.OPENCODE_URL ?? "http://localhost:4096";
  const mcpUrl = process.env.MCP_URL ?? `http://localhost:${process.env.MCP_DEV_PORT ?? "3001"}`;
  const meilisearchHost = process.env.MEILISEARCH_HOST ?? null;
  let searchStatus = {
    available: false,
    driver: null,
    url: meilisearchHost
  };
  try {
    const { createRequestContainer } = await import("@open-mercato/shared/lib/di/container");
    const container = await createRequestContainer();
    const searchService = container.resolve("searchService");
    const available = searchService.isStrategyAvailable("fulltext");
    searchStatus = {
      available,
      driver: available ? "meilisearch" : null,
      url: meilisearchHost
    };
  } catch {
  }
  try {
    const [health, mcp] = await Promise.all([client.health(), client.mcpStatus()]);
    return {
      status: "ok",
      opencode: health,
      mcp,
      search: searchStatus,
      url,
      mcpUrl
    };
  } catch (error) {
    return {
      status: "error",
      search: searchStatus,
      message: error instanceof Error ? error.message : "OpenCode not reachable",
      url,
      mcpUrl
    };
  }
}
function extractTextFromResponse(result) {
  if (!result || typeof result !== "object") return null;
  const message = result;
  if (!message.parts) return null;
  const textParts = message.parts.filter((p) => p.type === "text" && p.text);
  return textParts.map((p) => p.text).join("\n") || null;
}
function extractAllPartsFromResponse(result) {
  if (!result || typeof result !== "object") return [];
  const message = result;
  return message.parts || [];
}
function extractMetadataFromResponse(result) {
  if (!result || typeof result !== "object") return null;
  const message = result;
  if (!message.info) return null;
  return {
    modelID: message.info.modelID,
    providerID: message.info.providerID,
    tokens: message.info.tokens,
    timing: message.info.time
  };
}
async function handleOpenCodeMessageStreaming(request, onEvent) {
  const client = getClient();
  const { message, sessionId, model } = request;
  const startTime = Date.now();
  const usageStats = {
    toolCalls: 0,
    toolNames: [],
    totalInputTokens: 0,
    totalOutputTokens: 0,
    messageCount: 0
  };
  if (!message) {
    await onEvent({ type: "error", error: "Message is required" });
    return;
  }
  try {
    let session;
    if (sessionId) {
      session = await client.getSession(sessionId);
    } else {
      session = await client.createSession();
    }
    const targetSessionId = session.id;
    let unsubscribe = null;
    let emittedThinking = false;
    let wasBusy = false;
    let resolved = false;
    let lastActivityTime = Date.now();
    let heartbeatInterval = null;
    let lastMetadata = null;
    const cleanup = () => {
      if (heartbeatInterval) {
        clearInterval(heartbeatInterval);
        heartbeatInterval = null;
      }
    };
    const eventPromise = new Promise((resolve, reject) => {
      const timeout = setTimeout(() => {
        cleanup();
        unsubscribe?.();
        reject(new Error("OpenCode request timed out"));
      }, 3e5);
      heartbeatInterval = setInterval(async () => {
        if (resolved) return;
        const idleTime = Date.now() - lastActivityTime;
        if (idleTime >= 5e3 && wasBusy && !resolved) {
          try {
            const status = await client.getSessionStatus(targetSessionId);
            if (status.status === "busy") {
              return;
            }
            if (status.status === "waiting" && status.questionId) {
              const questions2 = await client.getPendingQuestions();
              const sessionQuestion2 = questions2.find((q) => q.id === status.questionId);
              if (sessionQuestion2) {
                await onEvent({ type: "question", question: sessionQuestion2 });
                lastActivityTime = Date.now();
                return;
              }
            }
            const questions = await client.getPendingQuestions();
            const sessionQuestion = questions.find((q) => q.sessionID === targetSessionId);
            if (sessionQuestion) {
              await onEvent({ type: "question", question: sessionQuestion });
              lastActivityTime = Date.now();
            } else if (status.status === "idle") {
              resolved = true;
              const durationMs = Date.now() - startTime;
              console.error(`[AI Usage] Session complete (heartbeat): sessionId=${targetSessionId.slice(0, 16)}... duration=${durationMs}ms tokens={in:${usageStats.totalInputTokens},out:${usageStats.totalOutputTokens}} toolCalls=${usageStats.toolCalls} tools=[${usageStats.toolNames.join(",")}] messages=${usageStats.messageCount}`);
              try {
                await onEvent({ type: "done", sessionId: targetSessionId });
              } catch (err) {
                console.error("[OpenCode SSE] Heartbeat: Failed to emit done event:", err);
              }
              cleanup();
              clearTimeout(timeout);
              unsubscribe?.();
              resolve();
            }
          } catch (err) {
            console.error("[OpenCode SSE] Heartbeat error:", err);
          }
        }
      }, 1e3);
      unsubscribe = client.subscribeToEvents(
        async (sseEvent) => {
          try {
            const { type, properties } = sseEvent;
            lastActivityTime = Date.now();
            const eventSessionId = properties.sessionID || properties.info?.sessionID || properties.part?.sessionID || properties.question?.sessionID || properties.session?.id || properties.status?.sessionID;
            if (eventSessionId && eventSessionId !== targetSessionId) {
              return;
            }
            switch (type) {
              case "question.asked": {
                await onEvent({ type: "debug", partType: "question-asked", data: properties });
                const questionFromEvent = properties.question;
                if (questionFromEvent && questionFromEvent.sessionID === targetSessionId) {
                  await onEvent({ type: "question", question: questionFromEvent });
                } else {
                  const questions = await client.getPendingQuestions();
                  const sessionQuestion = questions.find((q) => q.sessionID === targetSessionId);
                  if (sessionQuestion) {
                    await onEvent({ type: "question", question: sessionQuestion });
                  }
                }
                break;
              }
              case "session.status": {
                const status = properties.status;
                if (status?.type === "busy") {
                  wasBusy = true;
                  if (!emittedThinking) {
                    emittedThinking = true;
                    await onEvent({ type: "thinking" });
                  }
                } else if (status?.type === "waiting" && !resolved) {
                  const questions = await client.getPendingQuestions();
                  const sessionQuestion = status.questionId ? questions.find((q) => q.id === status.questionId) : questions.find((q) => q.sessionID === targetSessionId);
                  if (sessionQuestion) {
                    await onEvent({ type: "question", question: sessionQuestion });
                    lastActivityTime = Date.now();
                  }
                } else if (status?.type === "idle" && wasBusy && !resolved) {
                  const endTime = Date.now();
                  if (lastMetadata) {
                    await onEvent({
                      type: "metadata",
                      model: lastMetadata.model,
                      provider: lastMetadata.provider,
                      tokens: lastMetadata.tokens,
                      durationMs: endTime - startTime
                    });
                  }
                  const questions = await client.getPendingQuestions();
                  const sessionQuestion = questions.find((q) => q.sessionID === targetSessionId);
                  if (sessionQuestion) {
                    await onEvent({ type: "question", question: sessionQuestion });
                    lastActivityTime = Date.now();
                  } else {
                    setTimeout(async () => {
                      try {
                        if (resolved) {
                          return;
                        }
                        const finalQuestions = await client.getPendingQuestions();
                        const finalQuestion = finalQuestions.find((q) => q.sessionID === targetSessionId);
                        if (finalQuestion) {
                          await onEvent({ type: "question", question: finalQuestion });
                          lastActivityTime = Date.now();
                        } else {
                          resolved = true;
                          const durationMs = Date.now() - startTime;
                          console.error(`[AI Usage] Session complete: sessionId=${targetSessionId.slice(0, 16)}... duration=${durationMs}ms tokens={in:${usageStats.totalInputTokens},out:${usageStats.totalOutputTokens}} toolCalls=${usageStats.toolCalls} tools=[${usageStats.toolNames.join(",")}] messages=${usageStats.messageCount}`);
                          await onEvent({ type: "done", sessionId: targetSessionId });
                          cleanup();
                          clearTimeout(timeout);
                          unsubscribe?.();
                          resolve();
                        }
                      } catch (err) {
                        console.error("[OpenCode SSE] Error in timeout callback:", err);
                        if (!resolved) {
                          resolved = true;
                          try {
                            await onEvent({ type: "done", sessionId: targetSessionId });
                          } catch (e2) {
                            console.error("[OpenCode SSE] Failed to emit done event:", e2);
                          }
                          cleanup();
                          clearTimeout(timeout);
                          unsubscribe?.();
                          resolve();
                        }
                      }
                    }, 2e3);
                  }
                }
                break;
              }
              case "message.updated": {
                const info = properties.info;
                if (info.role === "assistant") {
                  if (info.error) {
                    cleanup();
                    clearTimeout(timeout);
                    unsubscribe?.();
                    await onEvent({
                      type: "error",
                      error: `${info.error.name}: ${info.error.message || "Unknown error"}`
                    });
                    resolve();
                    return;
                  }
                  if (info.time?.completed) {
                    lastMetadata = {
                      model: info.modelID,
                      provider: info.providerID,
                      tokens: info.tokens
                    };
                    usageStats.messageCount++;
                    if (info.tokens) {
                      usageStats.totalInputTokens += info.tokens.input || 0;
                      usageStats.totalOutputTokens += info.tokens.output || 0;
                      console.error(`[AI Usage] Tokens (message ${usageStats.messageCount}): input=${info.tokens.input} output=${info.tokens.output} model=${info.modelID || "unknown"}`);
                    }
                    await onEvent({
                      type: "debug",
                      partType: "message-completed",
                      data: { messageId: info.id, tokens: info.tokens }
                    });
                  }
                }
                break;
              }
              case "message.part.updated": {
                const part = properties.part;
                const delta = properties.delta;
                switch (part.type) {
                  case "text":
                    if (delta) {
                      await onEvent({ type: "text", content: delta });
                    }
                    break;
                  case "thinking":
                    console.error(`[OpenCode SSE] Thinking block received (${(delta || part.text || "").length} chars)`);
                    await onEvent({ type: "debug", partType: "thinking", data: { text: delta || part.text } });
                    break;
                  case "tool_use":
                    if (part.name) {
                      usageStats.toolCalls++;
                      usageStats.toolNames.push(part.name);
                      console.error(`[AI Usage] Tool call #${usageStats.toolCalls}: ${part.name}`);
                      await onEvent({
                        type: "tool-call",
                        id: part.id,
                        toolName: part.name,
                        args: part.input
                      });
                    }
                    break;
                  case "tool_result":
                    await onEvent({
                      type: "tool-result",
                      id: part.tool_use_id || part.id,
                      result: part.content
                    });
                    break;
                  case "step-start":
                  case "step-finish":
                    await onEvent({ type: "debug", partType: part.type, data: part });
                    break;
                }
                break;
              }
              case "message.part.delta": {
                const part = properties.part;
                const delta = properties.delta;
                if (delta && part?.type !== "thinking") {
                  await onEvent({ type: "text", content: delta });
                }
                break;
              }
            }
          } catch (err) {
            console.error("[OpenCode SSE] Error processing event:", err);
          }
        },
        (error) => {
          clearTimeout(timeout);
          reject(error);
        }
      );
    });
    client.sendMessage(session.id, message, { model }).catch((err) => {
      console.error("[OpenCode] Send error (SSE should handle):", err);
    });
    await eventPromise;
  } catch (error) {
    await onEvent({
      type: "error",
      error: error instanceof Error ? error.message : "OpenCode request failed"
    });
  }
}
async function handleOpenCodeAnswer(questionId, answer, sessionId, onEvent) {
  const client = getClient();
  try {
    await client.answerQuestion(questionId, answer);
    await onEvent({ type: "thinking" });
    const maxAttempts = 30;
    const pollInterval = 2e3;
    let sameQuestionWaitCount = 0;
    const maxSameQuestionWait = 5;
    for (let attempt = 0; attempt < maxAttempts; attempt++) {
      await new Promise((resolve) => setTimeout(resolve, pollInterval));
      const status = await client.getSessionStatus(sessionId);
      if (status.status === "idle" || status.status === "unknown") {
        await onEvent({ type: "done", sessionId });
        return;
      }
      if (status.status === "waiting" && status.questionId && status.questionId !== questionId) {
        const allQuestions = await client.getPendingQuestions();
        const newQuestion = allQuestions.find((q) => q.id === status.questionId);
        if (newQuestion) {
          await onEvent({ type: "question", question: newQuestion });
          return;
        }
      }
      if (status.status === "waiting" && status.questionId === questionId) {
        sameQuestionWaitCount++;
        if (sameQuestionWaitCount >= maxSameQuestionWait) {
          await onEvent({ type: "done", sessionId });
          return;
        }
      } else {
        sameQuestionWaitCount = 0;
      }
    }
    await onEvent({ type: "done", sessionId });
  } catch (error) {
    console.error("[OpenCode Answer] Error:", error);
    await onEvent({
      type: "error",
      error: error instanceof Error ? error.message : "Failed to answer question"
    });
  }
}
async function getPendingQuestions() {
  const client = getClient();
  return client.getPendingQuestions();
}
export {
  extractAllPartsFromResponse,
  extractMetadataFromResponse,
  extractTextFromResponse,
  getPendingQuestions,
  handleOpenCodeAnswer,
  handleOpenCodeHealth,
  handleOpenCodeMessage,
  handleOpenCodeMessageStreaming
};
//# sourceMappingURL=opencode-handlers.js.map
