/**
 * MONITORING_STATS Tool
 *
 * Get aggregated statistics for monitoring logs.
 * Supports both summary stats (backward-compatible) and timeseries queries.
 */

import { requireOrganization } from "@/core/studio-context";
import { flushMonitoringData } from "@/observability";
import { defineTool } from "../../core/define-tool";
import { z } from "zod";

export const MONITORING_STATS = defineTool({
  name: "MONITORING_STATS",
  description:
    "Get aggregated statistics (counts, latency, error rates) for tool call monitoring.",
  annotations: {
    title: "Get Monitoring Stats",
    readOnlyHint: true,
    destructiveHint: false,
    idempotentHint: true,
    openWorldHint: false,
  },
  inputSchema: z.object({
    startDate: z
      .string()
      .datetime()
      .optional()
      .describe("Filter by start date (ISO 8601 datetime string)"),
    endDate: z
      .string()
      .datetime()
      .optional()
      .describe("Filter by end date (ISO 8601 datetime string)"),
    interval: z
      .string()
      .regex(/^\d+[mhd]$/)
      .optional()
      .describe(
        "Bucket interval for timeseries data (e.g. 1m, 5m, 2h, 1d). When provided, returns timeseries array.",
      ),
    connectionIds: z
      .array(z.string())
      .max(100)
      .optional()
      .describe("Filter by specific connection IDs (max 100)"),
    excludeConnectionIds: z
      .array(z.string())
      .max(100)
      .optional()
      .describe("Exclude specific connection IDs (max 100)"),
    toolNames: z
      .array(z.string())
      .max(100)
      .optional()
      .describe("Filter by specific tool names (max 100)"),
    status: z
      .enum(["success", "error"])
      .optional()
      .describe("Filter metrics by execution status"),
    topN: z
      .number()
      .int()
      .min(1)
      .max(20)
      .optional()
      .describe(
        "When provided with interval, also return top tools and their timeseries",
      ),
    userIds: z
      .array(z.string())
      .max(100)
      .optional()
      .describe(
        "Filter LLM usage by user IDs (only applied when llmUsage is true)",
      ),
    llmUsage: z
      .boolean()
      .optional()
      .describe(
        "Aggregate LLM-call usage (tokens + USD cost) from raw log rows for the given connection. Returns token/cost totals and timeseries. Requires interval.",
      ),
  }),
  outputSchema: z.object({
    totalCalls: z.number().describe("Total number of tool calls"),
    errorRate: z
      .number()
      .optional()
      .describe("Error rate as a decimal (0 to 1)"),
    avgDurationMs: z.number().describe("Average call duration in milliseconds"),
    errorRatePercent: z
      .string()
      .optional()
      .describe("Error rate as a percentage string"),
    totalErrors: z.number().optional().describe("Total number of errors"),
    p50DurationMs: z
      .number()
      .optional()
      .describe("50th percentile duration in milliseconds"),
    p95DurationMs: z
      .number()
      .optional()
      .describe("95th percentile duration in milliseconds"),
    connectionBreakdown: z
      .array(
        z.object({
          connectionId: z.string(),
          calls: z.number(),
          errors: z.number(),
          errorRate: z.number(),
          avgDurationMs: z.number(),
        }),
      )
      .optional()
      .describe("Per-connection metric breakdown"),
    totalInputTokens: z
      .number()
      .optional()
      .describe("Total input tokens (LLM usage mode)"),
    totalOutputTokens: z
      .number()
      .optional()
      .describe("Total output tokens (LLM usage mode)"),
    totalTokens: z
      .number()
      .optional()
      .describe("Total tokens (LLM usage mode)"),
    totalCostUsd: z
      .number()
      .optional()
      .describe(
        "Total USD cost (LLM usage mode; only non-zero for providers that report cost)",
      ),
    topTools: z
      .array(
        z.object({
          toolName: z.string(),
          connectionId: z.string().nullable(),
          calls: z.number(),
          inputTokens: z.number().optional(),
          outputTokens: z.number().optional(),
          costUsd: z.number().optional(),
        }),
      )
      .optional()
      .describe("Top tools ranked by calls"),
    topToolsTimeseries: z
      .array(
        z.object({
          timestamp: z.string(),
          toolName: z.string(),
          calls: z.number(),
          errors: z.number(),
          avg: z.number(),
          p95: z.number(),
        }),
      )
      .optional()
      .describe("Per-tool metric timeseries for the top tools"),
    timeseries: z
      .array(
        z.object({
          timestamp: z.string(),
          calls: z.number(),
          errors: z.number(),
          errorRate: z.number(),
          avg: z.number(),
          p50: z.number(),
          p95: z.number(),
          inputTokens: z.number().optional(),
          outputTokens: z.number().optional(),
          totalTokens: z.number().optional(),
          costUsd: z.number().optional(),
        }),
      )
      .optional()
      .describe("Timeseries data points bucketed by interval"),
  }),
  handler: async (input, ctx) => {
    const org = requireOrganization(ctx);
    await ctx.access.check();
    await flushMonitoringData();

    if (input.interval && input.llmUsage) {
      return ctx.storage.monitoring.queryLlmUsageStats({
        organizationId: org.id,
        interval: input.interval,
        connectionId: input.connectionIds?.[0] ?? "decopilot",
        startDate: input.startDate ? new Date(input.startDate) : undefined,
        endDate: input.endDate ? new Date(input.endDate) : undefined,
        userIds: input.userIds,
        topN: input.topN,
      });
    }

    if (input.interval) {
      const stats = await ctx.storage.monitoring.queryMetricTimeseries({
        organizationId: org.id,
        interval: input.interval,
        startDate: input.startDate ? new Date(input.startDate) : undefined,
        endDate: input.endDate ? new Date(input.endDate) : undefined,
        filters: {
          connectionIds: input.connectionIds,
          excludeConnectionIds: input.excludeConnectionIds,
          toolNames: input.toolNames,
          status: input.status,
        },
      });

      if (!input.topN) {
        return stats;
      }

      const topTools =
        await ctx.storage.monitoring.queryMetricTopToolsTimeseries({
          organizationId: org.id,
          interval: input.interval,
          startDate: input.startDate ? new Date(input.startDate) : undefined,
          endDate: input.endDate ? new Date(input.endDate) : undefined,
          topN: input.topN,
          filters: {
            connectionIds: input.connectionIds,
            excludeConnectionIds: input.excludeConnectionIds,
            toolNames: input.toolNames,
            status: input.status,
          },
        });

      return {
        ...stats,
        topTools: topTools.topTools,
        topToolsTimeseries: topTools.timeseries,
      };
    }

    // Backward-compatible path
    const stats = await ctx.storage.monitoring.getStats({
      organizationId: org.id,
      startDate: input.startDate ? new Date(input.startDate) : undefined,
      endDate: input.endDate ? new Date(input.endDate) : undefined,
    });
    return { ...stats, errorRatePercent: (stats.errorRate * 100).toFixed(2) };
  },
});
