import { createClient } from "@supabase/supabase-js";
import { getWorkspaceMemory, saveWorkspaceMemory } from "./workspace-memory";
import type { ToolDefinition } from "./types";
import type { AIWorkspaceContext } from "./types";
import { isTaskDeadlineDueToday, isTaskDeadlineOverdue } from "@/lib/utils";

const PROJECT_COLORS = [
  "#6366f1",
  "#8b5cf6",
  "#ec4899",
  "#ef4444",
  "#f97316",
  "#eab308",
  "#22c55e",
  "#06b6d4",
  "#3b82f6",
  "#64748b",
];

const PROJECT_ICONS = [
  "📁",
  "🚀",
  "💼",
  "🎯",
  "📊",
  "🔧",
  "📝",
  "🎨",
  "💡",
  "🏠",
  "📦",
  "⚡",
  "🌟",
  "🔥",
  "💎",
  "🎮",
];

function getSupabaseAdmin() {
  return createClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.SUPABASE_SERVICE_ROLE_KEY!
  );
}

function pickProjectColor(name: string): string {
  const total = Array.from(name).reduce((sum, char) => sum + char.charCodeAt(0), 0);
  return PROJECT_COLORS[total % PROJECT_COLORS.length];
}

function pickProjectIcon(name: string): string {
  const total = Array.from(name).reduce((sum, char) => sum + char.charCodeAt(0), 0);
  return PROJECT_ICONS[total % PROJECT_ICONS.length];
}

function normalizeTitleCase(text: string): string {
  return text
    .split(/\s+/)
    .filter(Boolean)
    .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
    .join(" ");
}

function parseDateString(str: string): string | undefined {
  const now = new Date();
  const lower = str.toLowerCase();

  if (lower === "today" || lower === "heute") return now.toISOString().split("T")[0];
  if (lower === "tomorrow" || lower === "morgen") {
    const d = new Date(now);
    d.setDate(d.getDate() + 1);
    return d.toISOString().split("T")[0];
  }

  const days = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];
  const germanDays = ["sonntag", "montag", "dienstag", "mittwoch", "donnerstag", "freitag", "samstag"];
  const dayIndex = days.indexOf(lower);
  const germanDayIndex = germanDays.indexOf(lower);
  const targetDay = dayIndex >= 0 ? dayIndex : germanDayIndex;

  if (targetDay >= 0) {
    const d = new Date(now);
    const diff = (targetDay - d.getDay() + 7) % 7 || 7;
    d.setDate(d.getDate() + diff);
    return d.toISOString().split("T")[0];
  }

  if (/^\d{4}-\d{2}-\d{2}$/.test(str)) return str;
  return undefined;
}

async function getAccessibleTeamIds(userId: string) {
  const supabase = getSupabaseAdmin();
  const [ownedTeams, membershipRows] = await Promise.all([
    supabase.from("teams").select("id").eq("owner_id", userId),
    supabase.from("team_members").select("team_id").eq("user_id", userId),
  ]);

  return Array.from(
    new Set([
      ...(ownedTeams.data || []).map((team) => team.id),
      ...(membershipRows.data || []).map((membership) => membership.team_id),
    ])
  );
}

async function resolveWorkspaceScope(
  userId: string,
  context?: AIWorkspaceContext
) {
  const accessibleTeamIds = await getAccessibleTeamIds(userId);
  const requestedTeamId = context?.activeTeamId || null;
  const effectiveTeamId =
    requestedTeamId && accessibleTeamIds.includes(requestedTeamId)
      ? requestedTeamId
      : null;

  return {
    accessibleTeamIds,
    effectiveTeamId,
  };
}

function dedupeById<T extends { id: string }>(items: T[]) {
  return Array.from(new Map(items.map((item) => [item.id, item])).values());
}

function matchesAccess(
  userId: string,
  teamIds: string[],
  item: { user_id: string; team_id: string | null }
) {
  return item.user_id === userId || (item.team_id ? teamIds.includes(item.team_id) : false);
}

async function resolveProject(
  userId: string,
  projectId?: string,
  projectName?: string,
  context?: AIWorkspaceContext
) {
  const supabase = getSupabaseAdmin();
  const { accessibleTeamIds, effectiveTeamId } = await resolveWorkspaceScope(userId, context);

  if (projectId) {
    const { data } = await supabase
      .from("projects")
      .select("id, name, team_id, user_id, goal, description, health, color, icon, created_at")
      .eq("id", projectId)
      .maybeSingle();

    if (data && matchesAccess(userId, accessibleTeamIds, data)) {
      return data;
    }

    return null;
  }

  if (!projectName) return null;

  const normalizedProjectName = projectName.trim().toLowerCase();
  const workspaceProjects = effectiveTeamId
    ? await supabase
        .from("projects")
        .select("id, name, team_id, user_id, goal, description, health, color, icon, created_at")
        .eq("team_id", effectiveTeamId)
        .limit(50)
    : await supabase
        .from("projects")
        .select("id, name, team_id, user_id, goal, description, health, color, icon, created_at")
        .eq("user_id", userId)
        .is("team_id", null)
        .limit(50);

  const candidates = dedupeById(
    ((workspaceProjects.data || []) as Array<Record<string, unknown> & { id: string }>)
  );

  const exactMatch = candidates.find(
    (project) => String(project.name || "").trim().toLowerCase() === normalizedProjectName
  );
  if (exactMatch) return exactMatch;

  const fuzzyMatch = candidates.find((project) =>
    String(project.name || "").trim().toLowerCase().includes(normalizedProjectName)
  );
  return fuzzyMatch || null;
}

async function resolveProjectIdWithContext(
  userId: string,
  projectId?: string,
  projectName?: string,
  context?: AIWorkspaceContext
) {
  const project = await resolveProject(userId, projectId, projectName, context);
  return project?.id ?? null;
}

async function resolveProjectTemplate(
  userId: string,
  templateId?: string,
  templateName?: string,
  context?: AIWorkspaceContext
) {
  const supabase = getSupabaseAdmin();
  const { accessibleTeamIds, effectiveTeamId } = await resolveWorkspaceScope(userId, context);

  if (templateId) {
    const { data } = await supabase
      .from("project_templates")
      .select("id, name, description, goal, color, icon, health, team_id, user_id")
      .eq("id", templateId)
      .maybeSingle();

    if (data && matchesAccess(userId, accessibleTeamIds, data)) {
      return data;
    }

    return null;
  }

  if (!templateName) return null;

  const normalizedTemplateName = templateName.trim().toLowerCase();
  const workspaceTemplates = effectiveTeamId
    ? await supabase
        .from("project_templates")
        .select("id, name, description, goal, color, icon, health, team_id, user_id")
        .eq("team_id", effectiveTeamId)
        .limit(50)
    : await supabase
        .from("project_templates")
        .select("id, name, description, goal, color, icon, health, team_id, user_id")
        .eq("user_id", userId)
        .is("team_id", null)
        .limit(50);

  const candidates = dedupeById(
    ((workspaceTemplates.data || []) as Array<Record<string, unknown> & { id: string }>)
  );

  const exactMatch = candidates.find(
    (template) =>
      String(template.name || "").trim().toLowerCase() === normalizedTemplateName
  );
  if (exactMatch) return exactMatch;

  const fuzzyMatch = candidates.find((template) =>
    String(template.name || "").trim().toLowerCase().includes(normalizedTemplateName)
  );
  return fuzzyMatch || null;
}

async function resolveNote(userId: string, noteId?: string, context?: AIWorkspaceContext) {
  if (!noteId) return null;
  const supabase = getSupabaseAdmin();
  const { accessibleTeamIds, effectiveTeamId } = await resolveWorkspaceScope(userId, context);

  const { data } = await supabase
    .from("notes")
    .select("id, title, content, team_id, user_id, project_id, linked_task_id, is_pinned, updated_at")
    .eq("id", noteId)
    .maybeSingle();

  if (
    data &&
    matchesAccess(userId, accessibleTeamIds, data) &&
    (!effectiveTeamId || data.team_id === effectiveTeamId)
  ) {
    return data;
  }

  return null;
}

function tipTapNodeToText(node: unknown): string {
  if (!node || typeof node !== "object") return "";

  if (Array.isArray(node)) {
    return node.map((item) => tipTapNodeToText(item)).filter(Boolean).join("\n");
  }

  const record = node as {
    text?: string;
    content?: unknown[];
    type?: string;
  };

  const ownText = typeof record.text === "string" ? record.text : "";
  const childText = Array.isArray(record.content)
    ? record.content.map((item) => tipTapNodeToText(item)).filter(Boolean).join(record.type === "paragraph" ? " " : "\n")
    : "";

  return [ownText, childText].filter(Boolean).join(ownText && childText ? " " : "");
}

function noteContentToTipTap(content?: string) {
  if (!content?.trim()) {
    return { type: "doc", content: [{ type: "paragraph", content: [] }] };
  }

  return {
    type: "doc",
    content: content.split("\n").map((line) => ({
      type: "paragraph",
      content: line ? [{ type: "text", text: line }] : [],
    })),
  };
}

function buildOffsetDeadlineIso(
  baseDateIso: string,
  offsetDays: number | null | undefined
) {
  if (typeof offsetDays !== "number" || !Number.isFinite(offsetDays)) {
    return null;
  }

  const base = new Date(`${baseDateIso}T00:00:00`);
  base.setDate(base.getDate() + Math.max(0, Math.round(offsetDays)));
  return base.toISOString();
}

export function getWorkspaceToolDefinitions(): ToolDefinition[] {
  return [
    {
      name: "get_stats",
      description: "Get high-level workspace statistics for the current user.",
      input_schema: { type: "object", properties: {}, additionalProperties: false },
    },
    {
      name: "get_day_context",
      description: "Get today's planning context including overdue tasks, due tasks, and events.",
      input_schema: { type: "object", properties: {}, additionalProperties: false },
    },
    {
      name: "list_projects",
      description: "List projects, optionally filtered by a search query.",
      input_schema: {
        type: "object",
        properties: {
          query: { type: "string" },
          limit: { type: "number" },
        },
        additionalProperties: false,
      },
    },
    {
      name: "list_tasks",
      description: "List tasks, optionally filtered by query, status, priority, project, or due timing.",
      input_schema: {
        type: "object",
        properties: {
          query: { type: "string" },
          status: { type: "string", enum: ["todo", "in_progress", "done", "cancelled"] },
          priority: { type: "string", enum: ["urgent", "high", "medium", "low", "none"] },
          project_id: { type: "string" },
          project_name: { type: "string" },
          overdue_only: { type: "boolean" },
          due_today_only: { type: "boolean" },
          limit: { type: "number" },
        },
        additionalProperties: false,
      },
    },
    {
      name: "list_notes",
      description: "List notes, optionally filtered by query or pinned status.",
      input_schema: {
        type: "object",
        properties: {
          query: { type: "string" },
          pinned_only: { type: "boolean" },
          limit: { type: "number" },
        },
        additionalProperties: false,
      },
    },
    {
      name: "list_events",
      description: "List upcoming events.",
      input_schema: {
        type: "object",
        properties: {
          upcoming_days: { type: "number" },
          limit: { type: "number" },
        },
        additionalProperties: false,
      },
    },
    {
      name: "list_project_templates",
      description: "List reusable project templates in the current workspace.",
      input_schema: {
        type: "object",
        properties: {
          query: { type: "string" },
          limit: { type: "number" },
        },
        additionalProperties: false,
      },
    },
    {
      name: "get_project_template_context",
      description: "Get a project template with its starter tasks.",
      input_schema: {
        type: "object",
        properties: {
          template_id: { type: "string" },
          template_name: { type: "string" },
        },
        additionalProperties: false,
      },
    },
    {
      name: "get_project_context",
      description: "Get a detailed project snapshot including goal, description, open tasks, and recent notes.",
      input_schema: {
        type: "object",
        properties: {
          project_id: { type: "string" },
          project_name: { type: "string" },
        },
        additionalProperties: false,
      },
    },
    {
      name: "get_note_context",
      description: "Get a note, its plain-text content, and related project/task context.",
      input_schema: {
        type: "object",
        properties: {
          note_id: { type: "string" },
        },
        required: ["note_id"],
        additionalProperties: false,
      },
    },
    {
      name: "get_weekly_summary_context",
      description: "Get a structured weekly workspace summary including completed work, overdue items, upcoming events, recent notes, and team activity.",
      input_schema: {
        type: "object",
        properties: {},
        additionalProperties: false,
      },
    },
    {
      name: "get_project_risk_context",
      description: "Get risk signals for a project including overdue, stale, urgent, and missing-deadline tasks plus recent activity.",
      input_schema: {
        type: "object",
        properties: {
          project_id: { type: "string" },
          project_name: { type: "string" },
        },
        additionalProperties: false,
      },
    },
    {
      name: "get_workspace_memory",
      description: "Read the saved workspace memory for the current personal or team workspace.",
      input_schema: {
        type: "object",
        properties: {},
        additionalProperties: false,
      },
    },
    {
      name: "save_workspace_memory",
      description: "Save durable workspace memory such as planning preferences, team conventions, or important standing context.",
      input_schema: {
        type: "object",
        properties: {
          memory: { type: "string" },
          source: { type: "string" },
        },
        required: ["memory"],
        additionalProperties: false,
      },
    },
    {
      name: "create_project_template_from_project",
      description: "Turn an existing project into a reusable template by copying its structure and top-level tasks.",
      input_schema: {
        type: "object",
        properties: {
          project_id: { type: "string" },
          project_name: { type: "string" },
          template_name: { type: "string" },
          include_completed_tasks: { type: "boolean" },
        },
        additionalProperties: false,
      },
    },
    {
      name: "create_project_from_template",
      description: "Apply a project template by creating a new project and all of its starter tasks.",
      input_schema: {
        type: "object",
        properties: {
          template_id: { type: "string" },
          template_name: { type: "string" },
          project_name: { type: "string" },
          start_date: { type: "string" },
        },
        additionalProperties: false,
      },
    },
    {
      name: "create_project_template",
      description: "Create a reusable project template with starter tasks. Use deadline_offset_days as days from project start, where 0 means immediately.",
      input_schema: {
        type: "object",
        properties: {
          name: { type: "string" },
          description: { type: "string" },
          goal: { type: "string" },
          color: { type: "string" },
          icon: { type: "string" },
          health: { type: "string", enum: ["on_track", "at_risk", "stuck"] },
          tasks: {
            type: "array",
            items: {
              type: "object",
              properties: {
                title: { type: "string" },
                description: { type: "string" },
                priority: {
                  type: "string",
                  enum: ["urgent", "high", "medium", "low", "none"],
                },
                deadline_offset_days: { type: "number" },
              },
              required: ["title"],
              additionalProperties: false,
            },
          },
        },
        required: ["name"],
        additionalProperties: false,
      },
    },
    {
      name: "create_project",
      description: "Create a new project.",
      input_schema: {
        type: "object",
        properties: {
          name: { type: "string" },
          color: { type: "string" },
          icon: { type: "string" },
        },
        required: ["name"],
        additionalProperties: false,
      },
    },
    {
      name: "create_task",
      description: "Create a new task, optionally linked to a project.",
      input_schema: {
        type: "object",
        properties: {
          title: { type: "string" },
          description: { type: "string" },
          priority: { type: "string", enum: ["urgent", "high", "medium", "low", "none"] },
          deadline: { type: "string" },
          project_id: { type: "string" },
          project_name: { type: "string" },
          team_id: { type: "string" },
        },
        required: ["title"],
        additionalProperties: false,
      },
    },
    {
      name: "create_tasks_batch",
      description: "Create multiple tasks in one step, useful for planning a project or turning notes into action items.",
      input_schema: {
        type: "object",
        properties: {
          tasks: {
            type: "array",
            items: {
              type: "object",
              properties: {
                title: { type: "string" },
                description: { type: "string" },
                priority: { type: "string", enum: ["urgent", "high", "medium", "low", "none"] },
                deadline: { type: "string" },
                project_id: { type: "string" },
                project_name: { type: "string" },
                team_id: { type: "string" },
              },
              required: ["title"],
              additionalProperties: false,
            },
          },
        },
        required: ["tasks"],
        additionalProperties: false,
      },
    },
    {
      name: "create_note",
      description: "Create a new note, optionally linked to a project.",
      input_schema: {
        type: "object",
        properties: {
          title: { type: "string" },
          content: { type: "string" },
          pinned: { type: "boolean" },
          project_id: { type: "string" },
          project_name: { type: "string" },
        },
        required: ["title"],
        additionalProperties: false,
      },
    },
  ];
}

export async function executeWorkspaceTool(
  name: string,
  args: Record<string, unknown>,
  userId: string,
  context?: AIWorkspaceContext
) {
  const supabase = getSupabaseAdmin();
  const { effectiveTeamId } = await resolveWorkspaceScope(userId, context);

  switch (name) {
    case "get_stats": {
      const tasksBase = effectiveTeamId
        ? supabase.from("tasks").select("id", { count: "exact" }).eq("team_id", effectiveTeamId)
        : supabase
            .from("tasks")
            .select("id", { count: "exact" })
            .eq("user_id", userId)
            .is("team_id", null);
      const doneBase = effectiveTeamId
        ? supabase.from("tasks").select("id", { count: "exact" }).eq("team_id", effectiveTeamId)
        : supabase
            .from("tasks")
            .select("id", { count: "exact" })
            .eq("user_id", userId)
            .is("team_id", null);
      const notesBase = effectiveTeamId
        ? supabase.from("notes").select("id", { count: "exact" }).eq("team_id", effectiveTeamId)
        : supabase
            .from("notes")
            .select("id", { count: "exact" })
            .eq("user_id", userId)
            .is("team_id", null);
      const projectsBase = effectiveTeamId
        ? supabase.from("projects").select("id", { count: "exact" }).eq("team_id", effectiveTeamId)
        : supabase
            .from("projects")
            .select("id", { count: "exact" })
            .eq("user_id", userId)
            .is("team_id", null);
      const eventsBase = effectiveTeamId
        ? supabase.from("events").select("id", { count: "exact" }).eq("team_id", effectiveTeamId)
        : supabase
            .from("events")
            .select("id", { count: "exact" })
            .eq("user_id", userId)
            .is("team_id", null);

      const [tasksRes, doneRes, notesRes, projectsRes, eventsRes] = await Promise.all([
        tasksBase.is("parent_task_id", null),
        doneBase.is("parent_task_id", null).eq("status", "done"),
        notesBase,
        projectsBase,
        eventsBase,
      ]);

      return {
        tasks_total: tasksRes.count || 0,
        tasks_completed: doneRes.count || 0,
        tasks_open: (tasksRes.count || 0) - (doneRes.count || 0),
        notes_total: notesRes.count || 0,
        projects_total: projectsRes.count || 0,
        events_total: eventsRes.count || 0,
      };
    }

    case "get_day_context": {
      const now = new Date();
      const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate()).toISOString();
      const todayEnd = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1).toISOString();
      const tomorrowEnd = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 2).toISOString();

      const buildTaskQuery = () =>
        effectiveTeamId
          ? supabase
              .from("tasks")
              .select("id, title, priority, deadline, status")
              .eq("team_id", effectiveTeamId)
          : supabase
              .from("tasks")
              .select("id, title, priority, deadline, status")
              .eq("user_id", userId)
              .is("team_id", null);
      const buildEventQuery = () =>
        effectiveTeamId
          ? supabase
              .from("events")
              .select("id, title, start_time, end_time")
              .eq("team_id", effectiveTeamId)
          : supabase
              .from("events")
              .select("id, title, start_time, end_time")
              .eq("user_id", userId)
              .is("team_id", null);

      const [taskContextRes, eventsRes] = await Promise.all([
        buildTaskQuery()
          .is("parent_task_id", null)
          .in("status", ["todo", "in_progress"])
          .not("deadline", "is", null)
          .lt("deadline", tomorrowEnd)
          .order("deadline")
          .limit(40),
        buildEventQuery()
          .gte("start_time", todayStart)
          .lt("start_time", todayEnd)
          .order("start_time")
          .limit(10),
      ]);

      const taskContext = (taskContextRes.data || []) as Array<{
        deadline: string | null;
        id: string;
        priority: string;
        status: string;
        title: string;
      }>;
      const overdueTasks = taskContext
        .filter((task) => task.deadline && isTaskDeadlineOverdue(task.deadline, now))
        .slice(0, 10);
      const dueTodayTasks = taskContext
        .filter(
          (task) =>
            task.deadline &&
            isTaskDeadlineDueToday(task.deadline, now) &&
            !isTaskDeadlineOverdue(task.deadline, now),
        )
        .slice(0, 10);
      const upcomingTasks = taskContext
        .filter(
          (task) =>
            task.deadline &&
            !isTaskDeadlineDueToday(task.deadline, now) &&
            !isTaskDeadlineOverdue(task.deadline, now),
        )
        .slice(0, 10);

      return {
        date: todayStart.slice(0, 10),
        overdue_tasks: overdueTasks,
        due_today_tasks: dueTodayTasks,
        upcoming_tasks: upcomingTasks,
        events_today: eventsRes.data || [],
      };
    }

    case "list_projects": {
      const query = typeof args.query === "string" ? args.query.trim() : "";
      const limit = typeof args.limit === "number" ? Math.min(Math.max(args.limit, 1), 25) : 10;

      let dbQuery = supabase
        .from("projects")
        .select("id, name, color, icon, created_at")
        .order("sort_order")
        .order("created_at", { ascending: false })
        .limit(limit);

      dbQuery = effectiveTeamId
        ? dbQuery.eq("team_id", effectiveTeamId)
        : dbQuery.eq("user_id", userId).is("team_id", null);

      if (query) dbQuery = dbQuery.ilike("name", `%${query}%`);
      const { data } = await dbQuery;
      return { projects: data || [] };
    }

    case "list_project_templates": {
      const query = typeof args.query === "string" ? args.query.trim() : "";
      const limit =
        typeof args.limit === "number" ? Math.min(Math.max(args.limit, 1), 25) : 10;

      let templateQuery = supabase
        .from("project_templates")
        .select("id, name, description, goal, color, icon, health, created_at")
        .order("created_at", { ascending: false })
        .limit(limit);

      templateQuery = effectiveTeamId
        ? templateQuery.eq("team_id", effectiveTeamId)
        : templateQuery.eq("user_id", userId).is("team_id", null);

      if (query) {
        templateQuery = templateQuery.or(
          `name.ilike.%${query}%,description.ilike.%${query}%,goal.ilike.%${query}%`
        );
      }

      const { data: templates } = await templateQuery;
      const templateRows = templates || [];

      if (templateRows.length === 0) {
        return { templates: [] };
      }

      const { data: templateTasks } = await supabase
        .from("project_template_tasks")
        .select("template_id")
        .in(
          "template_id",
          templateRows.map((template) => template.id)
        );

      const taskCounts = (templateTasks || []).reduce<Record<string, number>>(
        (acc, row) => {
          acc[row.template_id] = (acc[row.template_id] || 0) + 1;
          return acc;
        },
        {}
      );

      return {
        templates: templateRows.map((template) => ({
          ...template,
          starter_task_count: taskCounts[template.id] || 0,
        })),
      };
    }

    case "list_tasks": {
      const query = typeof args.query === "string" ? args.query.trim() : "";
      const status = typeof args.status === "string" ? args.status : undefined;
      const priority = typeof args.priority === "string" ? args.priority : undefined;
      const overdueOnly = Boolean(args.overdue_only);
      const dueTodayOnly = Boolean(args.due_today_only);
      const limit = typeof args.limit === "number" ? Math.min(Math.max(args.limit, 1), 25) : 10;
      const projectId = await resolveProjectIdWithContext(
        userId,
        typeof args.project_id === "string" ? args.project_id : undefined,
        typeof args.project_name === "string" ? args.project_name : undefined,
        context
      );

      let dbQuery = supabase
        .from("tasks")
        .select("id, title, priority, status, deadline, project_id, created_at")
        .is("parent_task_id", null)
        .order("created_at", { ascending: false })
        .limit(limit);

      dbQuery = effectiveTeamId
        ? dbQuery.eq("team_id", effectiveTeamId)
        : dbQuery.eq("user_id", userId).is("team_id", null);

      if (query) dbQuery = dbQuery.ilike("title", `%${query}%`);
      if (status) dbQuery = dbQuery.eq("status", status);
      if (priority) dbQuery = dbQuery.eq("priority", priority);
      if (projectId) dbQuery = dbQuery.eq("project_id", projectId);

      if (overdueOnly || dueTodayOnly) {
        const now = new Date();
        const tomorrowEnd = new Date(
          now.getFullYear(),
          now.getMonth(),
          now.getDate() + 2,
        ).toISOString();
        dbQuery = dbQuery.not("deadline", "is", null).lt("deadline", tomorrowEnd).limit(
          Math.max(limit * 4, 50),
        );
      }

      const { data } = await dbQuery;
      const tasks = (data || []) as Array<{
        created_at: string;
        deadline: string | null;
        id: string;
        priority: string;
        project_id: string | null;
        status: string;
        title: string;
      }>;

      if (overdueOnly) {
        return {
          tasks: tasks
            .filter((task) => task.deadline && isTaskDeadlineOverdue(task.deadline))
            .slice(0, limit),
        };
      }

      if (dueTodayOnly) {
        return {
          tasks: tasks
            .filter(
              (task) =>
                task.deadline &&
                isTaskDeadlineDueToday(task.deadline) &&
                !isTaskDeadlineOverdue(task.deadline),
            )
            .slice(0, limit),
        };
      }

      return { tasks };
    }

    case "list_notes": {
      const query = typeof args.query === "string" ? args.query.trim() : "";
      const pinnedOnly = Boolean(args.pinned_only);
      const limit = typeof args.limit === "number" ? Math.min(Math.max(args.limit, 1), 25) : 10;

      let dbQuery = supabase
        .from("notes")
        .select("id, title, is_pinned, updated_at")
        .order("updated_at", { ascending: false })
        .limit(limit);

      dbQuery = effectiveTeamId
        ? dbQuery.eq("team_id", effectiveTeamId)
        : dbQuery.eq("user_id", userId).is("team_id", null);

      if (query) dbQuery = dbQuery.ilike("title", `%${query}%`);
      if (pinnedOnly) dbQuery = dbQuery.eq("is_pinned", true);

      const { data } = await dbQuery;
      return { notes: data || [] };
    }

    case "list_events": {
      const upcomingDays = typeof args.upcoming_days === "number" ? Math.min(Math.max(args.upcoming_days, 1), 30) : 7;
      const limit = typeof args.limit === "number" ? Math.min(Math.max(args.limit, 1), 25) : 10;
      const now = new Date();
      const end = new Date(now.getFullYear(), now.getMonth(), now.getDate() + upcomingDays).toISOString();

      let dbQuery = supabase
        .from("events")
        .select("id, title, start_time, end_time")
        .gte("start_time", now.toISOString())
        .lte("start_time", end)
        .order("start_time")
        .limit(limit);

      dbQuery = effectiveTeamId
        ? dbQuery.eq("team_id", effectiveTeamId)
        : dbQuery.eq("user_id", userId).is("team_id", null);

      const { data } = await dbQuery;

      return { events: data || [] };
    }

    case "get_project_context": {
      const project = await resolveProject(
        userId,
        typeof args.project_id === "string" ? args.project_id : undefined,
        typeof args.project_name === "string" ? args.project_name : undefined,
        context
      );

      if (!project) return { error: "Project not found." };

      const [tasksRes, notesRes, linksRes] = await Promise.all([
        supabase
          .from("tasks")
          .select("id, title, status, priority, deadline")
          .eq("project_id", project.id)
          .is("parent_task_id", null)
          .order("created_at", { ascending: false })
          .limit(20),
        supabase
          .from("notes")
          .select("id, title, is_pinned, updated_at")
          .eq("project_id", project.id)
          .order("updated_at", { ascending: false })
          .limit(10),
        supabase
          .from("project_links")
          .select("id, title, url, kind")
          .eq("project_id", project.id)
          .order("sort_order")
          .limit(10),
      ]);

      const openTasks = (tasksRes.data || []).filter(
        (task) => task.status !== "done" && task.status !== "cancelled"
      );

      return {
        project: {
          id: project.id,
          name: project.name,
          goal: project.goal || "",
          description: project.description || "",
          health: project.health || "on_track",
          team_id: project.team_id || null,
        },
        open_tasks: openTasks,
        existing_task_titles: openTasks.map((task) => task.title),
        recent_notes: notesRes.data || [],
        links: linksRes.data || [],
      };
    }

    case "get_project_template_context": {
      const template = await resolveProjectTemplate(
        userId,
        typeof args.template_id === "string" ? args.template_id : undefined,
        typeof args.template_name === "string" ? args.template_name : undefined,
        context
      );

      if (!template) return { error: "Project template not found." };

      const { data: templateTasks } = await supabase
        .from("project_template_tasks")
        .select("id, title, description, priority, deadline_offset_days, sort_order")
        .eq("template_id", template.id)
        .order("sort_order")
        .order("created_at", { ascending: true });

      return {
        template: {
          id: template.id,
          name: template.name,
          description: template.description || "",
          goal: template.goal || "",
          color: template.color,
          icon: template.icon,
          health: template.health,
          team_id: template.team_id || null,
        },
        starter_tasks: templateTasks || [],
      };
    }

    case "get_note_context": {
      const note = await resolveNote(
        userId,
        typeof args.note_id === "string" ? args.note_id : undefined,
        context
      );

      if (!note) return { error: "Note not found." };

      const project = note.project_id
        ? await resolveProject(userId, note.project_id)
        : null;

      return {
        note: {
          id: note.id,
          title: note.title,
          project_id: note.project_id,
          linked_task_id: note.linked_task_id,
          is_pinned: note.is_pinned,
          updated_at: note.updated_at,
          team_id: note.team_id,
        },
        plain_text_content: tipTapNodeToText(note.content).trim(),
        project: project
          ? {
              id: project.id,
              name: project.name,
              goal: project.goal || "",
              description: project.description || "",
            }
          : null,
      };
    }

    case "get_weekly_summary_context": {
      const now = new Date();
      const weekStart = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 6).toISOString();
      const nextWeekEnd = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 7).toISOString();

      const buildTaskQuery = () =>
        effectiveTeamId
          ? supabase
              .from("tasks")
              .select("id, title, status, priority, deadline, completed_at, created_at")
              .eq("team_id", effectiveTeamId)
          : supabase
              .from("tasks")
              .select("id, title, status, priority, deadline, completed_at, created_at")
              .eq("user_id", userId)
              .is("team_id", null);
      const buildNoteQuery = () =>
        effectiveTeamId
          ? supabase
              .from("notes")
              .select("id, title, updated_at")
              .eq("team_id", effectiveTeamId)
          : supabase
              .from("notes")
              .select("id, title, updated_at")
              .eq("user_id", userId)
              .is("team_id", null);
      const buildEventQuery = () =>
        effectiveTeamId
          ? supabase
              .from("events")
              .select("id, title, start_time, end_time")
              .eq("team_id", effectiveTeamId)
          : supabase
              .from("events")
              .select("id, title, start_time, end_time")
              .eq("user_id", userId)
              .is("team_id", null);

      const [completedRes, openRes, overdueRes, notesRes, eventsRes, auditRes] = await Promise.all([
        buildTaskQuery()
          .is("parent_task_id", null)
          .eq("status", "done")
          .gte("completed_at", weekStart)
          .order("completed_at", { ascending: false })
          .limit(12),
        buildTaskQuery()
          .is("parent_task_id", null)
          .in("status", ["todo", "in_progress"])
          .order("created_at", { ascending: false })
          .limit(20),
        buildTaskQuery()
          .is("parent_task_id", null)
          .in("status", ["todo", "in_progress"])
          .not("deadline", "is", null)
          .lt("deadline", nextWeekEnd)
          .order("deadline")
          .limit(40),
        buildNoteQuery().gte("updated_at", weekStart).order("updated_at", { ascending: false }).limit(8),
        buildEventQuery().gte("start_time", now.toISOString()).lte("start_time", nextWeekEnd).order("start_time").limit(8),
        effectiveTeamId
          ? supabase
              .from("team_audit_log")
              .select("id, action, title, created_at")
              .eq("team_id", effectiveTeamId)
              .gte("created_at", weekStart)
              .order("created_at", { ascending: false })
              .limit(12)
          : Promise.resolve({ data: [] }),
      ]);

      const openTasks = openRes.data || [];
      const highPriorityOpenTasks = openTasks.filter(
        (task) => task.priority === "urgent" || task.priority === "high"
      );

      return {
        workspace: {
          type: effectiveTeamId ? "team" : "personal",
          team_id: effectiveTeamId,
          period_start: weekStart.slice(0, 10),
          period_end: now.toISOString().slice(0, 10),
        },
        completed_tasks: completedRes.data || [],
        open_tasks: openTasks,
        high_priority_open_tasks: highPriorityOpenTasks,
        overdue_tasks:
          ((overdueRes.data || []) as Array<{ deadline: string | null }>)
            .filter((task) => task.deadline && isTaskDeadlineOverdue(task.deadline, now))
            .slice(0, 10),
        recent_notes: notesRes.data || [],
        upcoming_events: eventsRes.data || [],
        recent_team_activity: auditRes.data || [],
      };
    }

    case "get_project_risk_context": {
      const project = await resolveProject(
        userId,
        typeof args.project_id === "string" ? args.project_id : undefined,
        typeof args.project_name === "string" ? args.project_name : undefined,
        context
      );

      if (!project) return { error: "Project not found." };

      const now = new Date();
      const staleThreshold = new Date(Date.now() - 1000 * 60 * 60 * 24 * 7).toISOString();

      const [tasksRes, notesRes, activityRes] = await Promise.all([
        supabase
          .from("tasks")
          .select("id, title, status, priority, deadline, updated_at")
          .eq("project_id", project.id)
          .is("parent_task_id", null)
          .order("updated_at", { ascending: false })
          .limit(50),
        supabase
          .from("notes")
          .select("id, title, updated_at")
          .eq("project_id", project.id)
          .order("updated_at", { ascending: false })
          .limit(10),
        supabase
          .from("project_activity")
          .select("id, action, title, created_at, details")
          .eq("project_id", project.id)
          .order("created_at", { ascending: false })
          .limit(12),
      ]);

      const tasks = tasksRes.data || [];
      const openTasks = tasks.filter((task) => task.status !== "done" && task.status !== "cancelled");
      const overdueTasks = openTasks.filter((task) => task.deadline && isTaskDeadlineOverdue(task.deadline, now));
      const staleTasks = openTasks.filter((task) => task.updated_at < staleThreshold);
      const urgentTasks = openTasks.filter(
        (task) => task.priority === "urgent" || task.priority === "high"
      );
      const noDeadlineTasks = openTasks.filter((task) => !task.deadline);

      let suggestedRisk: "on_track" | "at_risk" | "stuck" = "on_track";
      if (overdueTasks.length >= 5 || staleTasks.length >= 4) {
        suggestedRisk = "stuck";
      } else if (overdueTasks.length >= 2 || urgentTasks.length >= 4 || noDeadlineTasks.length >= 5) {
        suggestedRisk = "at_risk";
      }

      const signals = [
        overdueTasks.length > 0 ? `${overdueTasks.length} overdue open tasks` : null,
        staleTasks.length > 0 ? `${staleTasks.length} stale tasks untouched for 7+ days` : null,
        urgentTasks.length > 0 ? `${urgentTasks.length} urgent or high-priority open tasks` : null,
        noDeadlineTasks.length > 0 ? `${noDeadlineTasks.length} open tasks without deadlines` : null,
      ].filter(Boolean);

      return {
        project: {
          id: project.id,
          name: project.name,
          health: project.health,
          goal: project.goal || "",
          description: project.description || "",
          team_id: project.team_id || null,
        },
        suggested_risk: suggestedRisk,
        risk_signals: signals,
        overdue_tasks: overdueTasks,
        stale_tasks: staleTasks,
        urgent_open_tasks: urgentTasks,
        no_deadline_open_tasks: noDeadlineTasks,
        recent_notes: notesRes.data || [],
        recent_activity: activityRes.data || [],
      };
    }

    case "get_workspace_memory": {
      const memory = await getWorkspaceMemory(userId, context);
      return {
        memory: memory?.memory || "",
        source: memory?.source || null,
        updated_at: memory?.updated_at || null,
        workspace_type: effectiveTeamId ? "team" : "personal",
        team_id: effectiveTeamId,
      };
    }

    case "save_workspace_memory": {
      const memory = typeof args.memory === "string" ? args.memory : "";
      const saved = await saveWorkspaceMemory({
        userId,
        memory,
        source: typeof args.source === "string" ? args.source : "assistant",
        context,
      });

      return {
        memory: saved?.memory || "",
        source: saved?.source || null,
        updated_at: saved?.updated_at || null,
        workspace_type: effectiveTeamId ? "team" : "personal",
        team_id: effectiveTeamId,
      };
    }

    case "create_project_template_from_project": {
      const project = await resolveProject(
        userId,
        typeof args.project_id === "string" ? args.project_id : undefined,
        typeof args.project_name === "string" ? args.project_name : undefined,
        context
      );

      if (!project) return { error: "Project not found." };

      const includeCompletedTasks = Boolean(args.include_completed_tasks);
      const templateName =
        typeof args.template_name === "string" && args.template_name.trim()
          ? normalizeTitleCase(args.template_name.trim())
          : normalizeTitleCase(`${project.name} Template`);

      const { data: projectTasks, error: tasksError } = await supabase
        .from("tasks")
        .select("id, title, description, priority, status, deadline")
        .eq("project_id", project.id)
        .is("parent_task_id", null)
        .order("sort_order")
        .order("created_at", { ascending: true });

      if (tasksError) return { error: tasksError.message };

      const sourceTasks = (projectTasks || []).filter((task) => {
        if (task.status === "cancelled") return false;
        if (!includeCompletedTasks && task.status === "done") return false;
        return true;
      });

      const projectStart = project.created_at
        ? new Date(project.created_at)
        : new Date();

      const { data: template, error: templateError } = await supabase
        .from("project_templates")
        .insert({
          user_id: userId,
          team_id: project.team_id || effectiveTeamId,
          name: templateName,
          description: project.description || "",
          goal: project.goal || "",
          color: project.color || pickProjectColor(templateName),
          icon: project.icon || pickProjectIcon(templateName),
          health: project.health || "on_track",
        })
        .select("id, name, description, goal, color, icon, health")
        .single();

      if (templateError) return { error: templateError.message };

      let starterTasks: Array<Record<string, unknown>> = [];
      if (sourceTasks.length > 0) {
        const { data: insertedTasks, error: insertTasksError } = await supabase
          .from("project_template_tasks")
          .insert(
            sourceTasks.map((task, index) => {
              const deadlineOffset =
                task.deadline
                  ? Math.max(
                      0,
                      Math.round(
                        (new Date(task.deadline).getTime() -
                          projectStart.getTime()) /
                          (1000 * 60 * 60 * 24)
                      )
                    )
                  : null;

              return {
                template_id: template.id,
                user_id: userId,
                team_id: project.team_id || effectiveTeamId,
                title: task.title,
                description: task.description || null,
                priority: task.priority || "none",
                deadline_offset_days: deadlineOffset,
                sort_order: index,
              };
            })
          )
          .select(
            "id, title, description, priority, deadline_offset_days, sort_order"
          );

        if (insertTasksError) return { error: insertTasksError.message };
        starterTasks = insertedTasks || [];
      }

      return {
        template,
        source_project: {
          id: project.id,
          name: project.name,
        },
        created_starter_task_count: starterTasks.length,
        starter_tasks: starterTasks,
      };
    }

    case "create_project_from_template": {
      const template = await resolveProjectTemplate(
        userId,
        typeof args.template_id === "string" ? args.template_id : undefined,
        typeof args.template_name === "string" ? args.template_name : undefined,
        context
      );

      if (!template) return { error: "Project template not found." };

      const startDate =
        typeof args.start_date === "string" && args.start_date.trim()
          ? parseDateString(args.start_date.trim()) || args.start_date.trim()
          : new Date().toISOString().slice(0, 10);

      const projectName =
        typeof args.project_name === "string" && args.project_name.trim()
          ? normalizeTitleCase(args.project_name.trim())
          : normalizeTitleCase(template.name);

      const { data: createdProject, error: projectError } = await supabase
        .from("projects")
        .insert({
          user_id: userId,
          team_id: template.team_id || effectiveTeamId,
          name: projectName,
          description: template.description || "",
          goal: template.goal || "",
          color: template.color || pickProjectColor(projectName),
          icon: template.icon || pickProjectIcon(projectName),
          health: template.health || "on_track",
        })
        .select("id, name, description, goal, color, icon, health, team_id")
        .single();

      if (projectError) return { error: projectError.message };

      const { data: templateTasks, error: templateTasksError } = await supabase
        .from("project_template_tasks")
        .select("id, title, description, priority, deadline_offset_days, sort_order")
        .eq("template_id", template.id)
        .order("sort_order")
        .order("created_at", { ascending: true });

      if (templateTasksError) return { error: templateTasksError.message };

      let createdTasks: Array<Record<string, unknown>> = [];
      if ((templateTasks || []).length > 0) {
        const { data: insertedTasks, error: createTasksError } = await supabase
          .from("tasks")
          .insert(
            (templateTasks || []).map((task) => ({
              user_id: userId,
              team_id: createdProject.team_id,
              project_id: createdProject.id,
              title: task.title,
              description: task.description || null,
              priority: task.priority || "none",
              status: "todo",
              deadline: buildOffsetDeadlineIso(
                startDate,
                task.deadline_offset_days
              ),
              sort_order: task.sort_order || 0,
            }))
          )
          .select("id, title, priority, deadline, project_id");

        if (createTasksError) return { error: createTasksError.message };
        createdTasks = insertedTasks || [];
      }

      return {
        template: {
          id: template.id,
          name: template.name,
        },
        project: createdProject,
        created_task_count: createdTasks.length,
        tasks: createdTasks,
        start_date: startDate,
      };
    }

    case "create_project_template": {
      const rawName = typeof args.name === "string" ? args.name.trim() : "";
      if (!rawName) return { error: "Template name is required." };

      const nameValue = normalizeTitleCase(rawName);
      const { data: template, error: templateError } = await supabase
        .from("project_templates")
        .insert({
          user_id: userId,
          team_id: effectiveTeamId,
          name: nameValue,
          description:
            typeof args.description === "string" ? args.description.trim() : "",
          goal: typeof args.goal === "string" ? args.goal.trim() : "",
          color:
            typeof args.color === "string" && args.color.trim()
              ? args.color.trim()
              : pickProjectColor(nameValue),
          icon:
            typeof args.icon === "string" && args.icon.trim()
              ? args.icon.trim()
              : pickProjectIcon(nameValue),
          health:
            typeof args.health === "string" &&
            ["on_track", "at_risk", "stuck"].includes(args.health)
              ? args.health
              : "on_track",
        })
        .select("id, name, description, goal, color, icon, health")
        .single();

      if (templateError) return { error: templateError.message };

      const rawTasks = Array.isArray(args.tasks) ? args.tasks : [];
      const validTasks = rawTasks
        .filter(
          (task): task is Record<string, unknown> =>
            Boolean(task) && typeof task === "object"
        )
        .slice(0, 20)
        .map((task) => ({
          title:
            typeof task.title === "string" ? task.title.trim() : "",
          description:
            typeof task.description === "string"
              ? task.description.trim() || null
              : null,
          priority:
            typeof task.priority === "string" &&
            ["urgent", "high", "medium", "low", "none"].includes(task.priority)
              ? task.priority
              : "none",
          deadline_offset_days:
            typeof task.deadline_offset_days === "number" &&
            Number.isFinite(task.deadline_offset_days)
              ? Math.round(task.deadline_offset_days)
              : null,
        }))
        .filter((task) => task.title.length > 0);

      let starterTasks: Array<Record<string, unknown>> = [];

      if (validTasks.length > 0) {
        const { data: insertedTasks, error: tasksError } = await supabase
          .from("project_template_tasks")
          .insert(
            validTasks.map((task, index) => ({
              template_id: template.id,
              user_id: userId,
              team_id: effectiveTeamId,
              title: task.title,
              description: task.description,
              priority: task.priority,
              deadline_offset_days: task.deadline_offset_days,
              sort_order: index,
            }))
          )
          .select(
            "id, title, description, priority, deadline_offset_days, sort_order"
          );

        if (tasksError) return { error: tasksError.message };
        starterTasks = insertedTasks || [];
      }

      return {
        template,
        created_starter_task_count: starterTasks.length,
        starter_tasks: starterTasks,
      };
    }

    case "create_project": {
      const rawName = typeof args.name === "string" ? args.name.trim() : "";
      if (!rawName) return { error: "Project name is required." };

      const nameValue = normalizeTitleCase(rawName);
      const { data, error } = await supabase
        .from("projects")
        .insert({
          user_id: userId,
          team_id: effectiveTeamId,
          name: nameValue,
          color: typeof args.color === "string" && args.color.trim() ? args.color.trim() : pickProjectColor(nameValue),
          icon: typeof args.icon === "string" && args.icon.trim() ? args.icon.trim() : pickProjectIcon(nameValue),
        })
        .select("id, name, color, icon")
        .single();

      if (error) return { error: error.message };
      return { project: data };
    }

    case "create_task": {
      const title = typeof args.title === "string" ? args.title.trim() : "";
      if (!title) return { error: "Task title is required." };

      const project = await resolveProject(
        userId,
        typeof args.project_id === "string" ? args.project_id : undefined,
        typeof args.project_name === "string" ? args.project_name : undefined,
        context
      );
      const accessibleTeamIds = (await resolveWorkspaceScope(userId, context)).accessibleTeamIds;
      const explicitTeamId =
        typeof args.team_id === "string" && accessibleTeamIds.includes(args.team_id)
          ? args.team_id
          : null;

      const parsedDeadline =
        typeof args.deadline === "string" && args.deadline.trim()
          ? parseDateString(args.deadline.trim()) || args.deadline.trim()
          : undefined;

      const { data, error } = await supabase
        .from("tasks")
        .insert({
          user_id: userId,
          title,
          description: typeof args.description === "string" ? args.description.trim() || null : null,
          priority: typeof args.priority === "string" ? args.priority : "none",
          project_id: project?.id || null,
          team_id: project?.team_id || explicitTeamId || effectiveTeamId,
          deadline: parsedDeadline ? new Date(`${parsedDeadline}T00:00:00`).toISOString() : null,
        })
        .select("id, title, priority, deadline, project_id")
        .single();

      if (error) return { error: error.message };
      return { task: data };
    }

    case "create_tasks_batch": {
      const rawTasks = Array.isArray(args.tasks) ? args.tasks : [];
      if (rawTasks.length === 0) return { error: "At least one task is required." };

      const accessibleTeamIds = (await resolveWorkspaceScope(userId, context)).accessibleTeamIds;
      const createdTasks: Array<Record<string, unknown>> = [];

      for (const rawTask of rawTasks.slice(0, 12)) {
        if (!rawTask || typeof rawTask !== "object") continue;
        const taskArgs = rawTask as Record<string, unknown>;
        const title = typeof taskArgs.title === "string" ? taskArgs.title.trim() : "";
        if (!title) continue;

        const project = await resolveProject(
          userId,
          typeof taskArgs.project_id === "string" ? taskArgs.project_id : undefined,
          typeof taskArgs.project_name === "string" ? taskArgs.project_name : undefined,
          context
        );

        const explicitTeamId =
          typeof taskArgs.team_id === "string" && accessibleTeamIds.includes(taskArgs.team_id)
            ? taskArgs.team_id
            : null;

        const parsedDeadline =
          typeof taskArgs.deadline === "string" && taskArgs.deadline.trim()
            ? parseDateString(taskArgs.deadline.trim()) || taskArgs.deadline.trim()
            : undefined;

        const { data, error } = await supabase
          .from("tasks")
          .insert({
            user_id: userId,
            title,
            description:
              typeof taskArgs.description === "string"
                ? taskArgs.description.trim() || null
                : null,
            priority:
              typeof taskArgs.priority === "string"
                ? taskArgs.priority
                : "none",
            project_id: project?.id || null,
            team_id: project?.team_id || explicitTeamId || effectiveTeamId,
            deadline: parsedDeadline
              ? new Date(`${parsedDeadline}T00:00:00`).toISOString()
              : null,
          })
          .select("id, title, priority, deadline, project_id")
          .single();

        if (!error && data) {
          createdTasks.push(data);
        }
      }

      return {
        created_count: createdTasks.length,
        tasks: createdTasks,
      };
    }

    case "create_note": {
      const title = typeof args.title === "string" ? args.title.trim() : "";
      if (!title) return { error: "Note title is required." };

      const projectId = await resolveProjectIdWithContext(
        userId,
        typeof args.project_id === "string" ? args.project_id : undefined,
        typeof args.project_name === "string" ? args.project_name : undefined,
        context
      );

      const { data, error } = await supabase
        .from("notes")
        .insert({
          user_id: userId,
          team_id: effectiveTeamId,
          title,
          content: noteContentToTipTap(typeof args.content === "string" ? args.content : undefined),
          is_pinned: Boolean(args.pinned),
          project_id: projectId,
        })
        .select("id, title, project_id, is_pinned")
        .single();

      if (error) return { error: error.message };
      return { note: data };
    }

    default:
      return { error: `Unknown tool: ${name}` };
  }
}
