/**
 * Token model performance tracking module.
 *
 * Aggregates per-model performance metrics (latency, cost, throughput) from
 * TokenMessage records and provides daily trend data for charting.
 */

import { db } from "@/lib/db";
import { logger } from "@/lib/logger";

/* ── Types ─────────────────────────────────────────────────────────── */

export interface ModelPerformance {
  model: string;
  messageCount: number;
  avgLatencyMs: number;
  p95LatencyMs: number;
  totalCostUsd: number;
  avgCostPerMessage: number;
  totalInputTokens: number;
  totalOutputTokens: number;
}

export interface DailyTrend {
  date: string; // YYYY-MM-DD
  model: string;
  messageCount: number;
  totalCostUsd: number;
  avgLatencyMs: number;
}

/* ── Per-model performance aggregation ─────────────────────────────── */

export async function getModelPerformance(userId: string): Promise<ModelPerformance[]> {
  const conversations = await db.tokenConversation.findMany({
    where: { userId },
    select: { id: true },
  });

  const conversationIds = conversations.map((c) => c.id);

  if (conversationIds.length === 0) {
    return [];
  }

  const messages = await db.tokenMessage.findMany({
    where: {
      conversationId: { in: conversationIds },
      role: "assistant",
      model: { not: null },
    },
    select: {
      model: true,
      costUsd: true,
      latencyMs: true,
      inputTokens: true,
      outputTokens: true,
    },
  });

  if (messages.length === 0) {
    return [];
  }

  // Group by model
  const grouped = new Map<
    string,
    { costs: number[]; latencies: number[]; inputTokens: number; outputTokens: number }
  >();

  for (const msg of messages) {
    const model = msg.model!;
    let bucket = grouped.get(model);
    if (!bucket) {
      bucket = { costs: [], latencies: [], inputTokens: 0, outputTokens: 0 };
      grouped.set(model, bucket);
    }
    bucket.costs.push(msg.costUsd);
    bucket.latencies.push(msg.latencyMs);
    bucket.inputTokens += msg.inputTokens;
    bucket.outputTokens += msg.outputTokens;
  }

  const results: ModelPerformance[] = [];

  for (const [model, bucket] of grouped) {
    const count = bucket.costs.length;
    const totalCost = bucket.costs.reduce((s, c) => s + c, 0);
    const avgLatency = bucket.latencies.reduce((s, l) => s + l, 0) / count;

    // P95 latency: sort ascending, pick 95th percentile index
    const sorted = [...bucket.latencies].sort((a, b) => a - b);
    const p95Index = Math.min(Math.ceil(count * 0.95) - 1, count - 1);
    const p95Latency = sorted[p95Index];

    results.push({
      model,
      messageCount: count,
      avgLatencyMs: Math.round(avgLatency),
      p95LatencyMs: p95Latency,
      totalCostUsd: roundCents(totalCost),
      avgCostPerMessage: roundCents(totalCost / count),
      totalInputTokens: bucket.inputTokens,
      totalOutputTokens: bucket.outputTokens,
    });
  }

  // Sort by message count descending
  results.sort((a, b) => b.messageCount - a.messageCount);

  logger.debug(
    { action: "token_performance_aggregated", user_id: userId, model_count: results.length },
    "Model performance aggregated",
  );

  return results;
}

/* ── Daily trend data for charting ─────────────────────────────────── */

export async function getModelTrends(userId: string, days: number = 30): Promise<DailyTrend[]> {
  const conversations = await db.tokenConversation.findMany({
    where: { userId },
    select: { id: true },
  });

  const conversationIds = conversations.map((c) => c.id);

  if (conversationIds.length === 0) {
    return [];
  }

  const since = new Date();
  since.setDate(since.getDate() - days);
  since.setHours(0, 0, 0, 0);

  const messages = await db.tokenMessage.findMany({
    where: {
      conversationId: { in: conversationIds },
      role: "assistant",
      model: { not: null },
      createdAt: { gte: since },
    },
    select: {
      model: true,
      costUsd: true,
      latencyMs: true,
      createdAt: true,
    },
  });

  if (messages.length === 0) {
    return [];
  }

  // Group by date + model
  const grouped = new Map<string, { costs: number[]; latencies: number[] }>();

  for (const msg of messages) {
    const dateStr = msg.createdAt.toISOString().slice(0, 10);
    const key = `${dateStr}|${msg.model!}`;
    let bucket = grouped.get(key);
    if (!bucket) {
      bucket = { costs: [], latencies: [] };
      grouped.set(key, bucket);
    }
    bucket.costs.push(msg.costUsd);
    bucket.latencies.push(msg.latencyMs);
  }

  const results: DailyTrend[] = [];

  for (const [key, bucket] of grouped) {
    const [date, model] = key.split("|");
    const count = bucket.costs.length;
    const totalCost = bucket.costs.reduce((s, c) => s + c, 0);
    const avgLatency = bucket.latencies.reduce((s, l) => s + l, 0) / count;

    results.push({
      date,
      model,
      messageCount: count,
      totalCostUsd: roundCents(totalCost),
      avgLatencyMs: Math.round(avgLatency),
    });
  }

  // Sort by date ascending, then model
  results.sort((a, b) => a.date.localeCompare(b.date) || a.model.localeCompare(b.model));

  logger.debug(
    { action: "token_trends_aggregated", user_id: userId, days, trend_count: results.length },
    "Model trends aggregated",
  );

  return results;
}

/* ── Format performance context for system prompt ──────────────────── */

export function formatPerformanceContext(perf: ModelPerformance[]): string {
  if (perf.length === 0) {
    return "No model performance data available yet.";
  }

  const top5 = perf.slice(0, 5);
  const lines: string[] = ["### Model Performance (Top 5)"];

  for (const p of top5) {
    lines.push(
      `- ${p.model}: ${p.messageCount} msgs, avg ${p.avgLatencyMs}ms, $${p.avgCostPerMessage.toFixed(4)}/msg`,
    );
  }

  return lines.join("\n");
}

/* ── Helpers ─────────────────────────────────────────────────────────── */

function roundCents(n: number): number {
  return Math.round(n * 10000) / 10000;
}
