import { getDefaultModel } from "./catalog";
import { getDecryptedApiKey } from "./settings";
import { generateWithAnthropic } from "./providers/anthropic";
import { generateWithMindavioCore } from "./providers/mindavio-core";
import { generateWithOpenAI } from "./providers/openai";
import { getWorkspaceMemory } from "./workspace-memory";
import { executeWorkspaceTool, getWorkspaceToolDefinitions } from "./workspace-tools";
import type { AIWorkspaceContext, ConversationMessage } from "./types";

function getSystemPrompt(customInstructions?: string, workspaceMemory?: string) {
  return [
    "You are Mindavio AI, the user's own productivity assistant inside their workspace.",
    "You are not 'the OpenAI API' or 'Claude'. You are the product AI for Mindavio.",
    "Answer naturally in the user's language.",
    "You can operate in advanced modes when useful: Future Self Simulator, Identity Engine, Reality Check AI, Energy Map, Personal Intelligence Layer, and Private Command AI.",
    "Future Self Simulator means projecting likely outcomes from the user's current behavior, systems, momentum, and risks over 3, 6, and 12 months.",
    "Identity Engine means translating goals, routines, focus behavior, and standards into identity statements, principles, and non-negotiables.",
    "Reality Check AI means being honest and precise about contradictions, self-deception, drift, waste, and hard tradeoffs. Do not be rude, but do not soften important truths.",
    "Energy Map means identifying what gives energy, drains energy, creates friction, or creates false productivity based on tasks, focus behavior, calendar load, and journals when available.",
    "Personal Intelligence Layer means connecting findings across modules such as tasks, projects, focus, achievements, finances, net worth, invoicing, reminders, vault, and notes to detect gaps, conflicts, duplicates, and unseen opportunities.",
    "Private Command AI means acting like a trusted chief of staff: cut through noise, name the real priorities, surface hidden risks, and recommend the minimum set of decisive next moves.",
    "Use tools whenever the user asks about their workspace, wants data, or wants actions performed.",
    "You can complete multiple actions in one request when it makes sense.",
    "If the user asks to create a project and a task together, create the project first and then the task linked to it.",
    "You can create reusable project templates with starter tasks when the user describes a workflow or asks for a template.",
    "You can also turn an existing project into a reusable template and apply a template to create a brand-new project with starter tasks.",
    "You can turn a project goal into a concrete task plan and turn a note or meeting note into action items.",
    "You can create weekly summaries, meeting summaries, and project risk snapshots when asked.",
    "When enough data exists, proactively synthesize across modules instead of answering narrowly from one page.",
    "If you infer something strategic, make it clear that it is an inference from workspace signals.",
    "When the user asks for a project template, include clear starter tasks and use deadline offsets when timing matters.",
    "When the user asks for several tasks at once, prefer the batch task tool.",
    "Use workspace memory to preserve durable preferences, conventions, and context. Only update workspace memory when the user explicitly asks you to remember something long-term or when they clearly approve it.",
    "Keep answers concise, practical, and explicit about outcomes.",
    workspaceMemory?.trim()
      ? `Current workspace memory: ${workspaceMemory.trim()}`
      : "",
    `Today's date is ${new Date().toISOString().slice(0, 10)}.`,
    customInstructions?.trim() ? `Additional instructions: ${customInstructions.trim()}` : "",
  ]
    .filter(Boolean)
    .join(" ");
}

function getFallbackText() {
  return "Mindavio Core could not answer right now. Configure MINDBOARD_CORE_URL for your server model, or connect OpenAI/Claude in Settings.";
}

async function generateOnce(input: {
  provider: "mindavio_core" | "openai" | "anthropic";
  apiKey?: string;
  model: string;
  systemPrompt: string;
  messages: ConversationMessage[];
  toolResults?: Array<{ callId: string; output: Record<string, unknown> }>;
  providerState?: string;
}) {
  if (input.provider === "mindavio_core") {
    return generateWithMindavioCore({
      apiKey: "",
      model: input.model,
      systemPrompt: input.systemPrompt,
      messages: input.messages,
      tools: getWorkspaceToolDefinitions(),
      toolResults: input.toolResults,
      providerState: input.providerState,
    });
  }

  if (input.provider === "anthropic") {
    return generateWithAnthropic({
      apiKey: input.apiKey!,
      model: input.model,
      systemPrompt: input.systemPrompt,
      messages: input.messages,
      tools: getWorkspaceToolDefinitions(),
      toolResults: input.toolResults,
      providerState: input.providerState,
    });
  }

  return generateWithOpenAI({
    apiKey: input.apiKey!,
    model: input.model,
    systemPrompt: input.systemPrompt,
    messages: input.messages,
    tools: getWorkspaceToolDefinitions(),
    toolResults: input.toolResults,
    providerState: input.providerState,
  });
}

export async function runMindavioAI(
  userId: string,
  messages: ConversationMessage[],
  context?: AIWorkspaceContext
) {
  const config = await getDecryptedApiKey(userId);
  const workspaceMemory = await getWorkspaceMemory(userId, context);
  const selectedProvider = config?.provider || "mindavio_core";
  const provider =
    selectedProvider === "openai" && config?.openaiApiKey
      ? "openai"
      : selectedProvider === "anthropic" && config?.anthropicApiKey
      ? "anthropic"
      : "mindavio_core";
  const model =
    provider === "openai"
      ? config?.model || getDefaultModel("openai")
      : provider === "anthropic"
      ? config?.model || getDefaultModel("anthropic")
      : config?.provider === "mindavio_core" && config?.model
      ? config.model
      : getDefaultModel("mindavio_core");
  const systemPrompt = getSystemPrompt(config?.customInstructions, workspaceMemory?.memory);
  const apiKey =
    provider === "openai"
      ? config?.openaiApiKey || undefined
      : provider === "anthropic"
      ? config?.anthropicApiKey || undefined
      : undefined;

  let providerState: string | undefined;
  let toolResults: Array<{ callId: string; output: Record<string, unknown> }> | undefined;
  let finalText = "";

  try {
    for (let step = 0; step < 6; step += 1) {
      const result = await generateOnce({
        provider,
        apiKey,
        model,
        systemPrompt,
        messages,
        providerState,
        toolResults,
      });

      providerState = result.providerState;
      if (result.text) finalText = result.text;

      if (result.toolCalls.length === 0) {
        return {
          text: finalText || "I couldn't generate a response just now.",
          provider,
          model,
        };
      }

      toolResults = await Promise.all(
        result.toolCalls.map(async (toolCall) => ({
          callId: toolCall.id,
          output: await executeWorkspaceTool(toolCall.name, toolCall.arguments, userId, context),
        }))
      );
    }

    return {
      text: finalText || "I reached the tool step limit. Please try rephrasing your request.",
      provider,
      model,
    };
  } catch {
    if (provider !== "mindavio_core") {
      try {
        let fallbackProviderState: string | undefined;
        let fallbackToolResults: Array<{ callId: string; output: Record<string, unknown> }> | undefined;
        let fallbackText = "";

        for (let step = 0; step < 6; step += 1) {
          const result = await generateOnce({
            provider: "mindavio_core",
            model: getDefaultModel("mindavio_core"),
            systemPrompt,
            messages,
            providerState: fallbackProviderState,
            toolResults: fallbackToolResults,
          });

          fallbackProviderState = result.providerState;
          if (result.text) fallbackText = result.text;

          if (result.toolCalls.length === 0) {
            return {
              text: fallbackText || "I couldn't generate a response just now.",
              provider: "mindavio_core",
              model: getDefaultModel("mindavio_core"),
            };
          }

          fallbackToolResults = await Promise.all(
            result.toolCalls.map(async (toolCall) => ({
              callId: toolCall.id,
              output: await executeWorkspaceTool(toolCall.name, toolCall.arguments, userId, context),
            }))
          );
        }
      } catch {}
    }

    return {
      text: getFallbackText(),
      provider: "mindavio_core",
      model: getDefaultModel("mindavio_core"),
    };
  }
}
