/**
 * Feedback Statistics
 *
 * Aggregate statistics computed from stored feedback:
 * - Approval rates (overall, by server type, by stage)
 * - Average scores per dimension
 * - Trends over time
 * - Volume metrics
 */

import type {
  EnrichedFeedbackEvent,
  QualityDimension,
  DimensionScore,
} from "../types.js";
import { loadFeedbackEvents } from "./feedback-store.js";

// ═══════════════════════════════════════════════════════════════
// AGGREGATE TYPES
// ═══════════════════════════════════════════════════════════════

export interface AggregateStats {
  totalEvents: number;
  totalDecisions: number;
  approvalRate: number;
  rejectionRate: number;
  needsWorkRate: number;
  avgTimeToDecisionMs: number;
  avgQualityScore: number;
  byServerType: Record<
    string,
    { approved: number; rejected: number; needsWork: number; total: number; rate: number }
  >;
  byDimension: Record<
    string,
    { avg: number; min: number; max: number; count: number; trend: number }
  >;
  byWeek: WeeklyStats[];
  volumeByDay: Record<string, number>;
}

export interface WeeklyStats {
  weekStart: string;
  eventCount: number;
  approvalRate: number;
  avgScore: number;
  avgTimeToDecisionMs: number;
}

// ═══════════════════════════════════════════════════════════════
// STATS COMPUTATION
// ═══════════════════════════════════════════════════════════════

/**
 * Compute aggregate statistics from stored feedback events.
 */
export async function computeStats(
  dataDir: string,
  days: number = 30
): Promise<AggregateStats> {
  const events = await loadFeedbackEvents(dataDir, days);
  return computeStatsFromEvents(events);
}

/**
 * Compute stats from a pre-loaded array of events (for reuse).
 */
export function computeStatsFromEvents(
  events: EnrichedFeedbackEvent[]
): AggregateStats {
  if (events.length === 0) {
    return {
      totalEvents: 0,
      totalDecisions: 0,
      approvalRate: 0,
      rejectionRate: 0,
      needsWorkRate: 0,
      avgTimeToDecisionMs: 0,
      avgQualityScore: 0,
      byServerType: {},
      byDimension: {},
      byWeek: [],
      volumeByDay: {},
    };
  }

  // ─── Decision counts ───
  const decisions = events.filter((e) => e.feedback.decision);
  const approved = decisions.filter(
    (e) => e.feedback.decision?.decision === "approved"
  ).length;
  const rejected = decisions.filter(
    (e) => e.feedback.decision?.decision === "rejected"
  ).length;
  const needsWork = decisions.filter(
    (e) => e.feedback.decision?.decision === "needs_work"
  ).length;

  // ─── Time to decision ───
  const decisionTimes = events
    .map((e) => e.meta.timeToDecisionMs)
    .filter((t) => t > 0);
  const avgTimeToDecisionMs =
    decisionTimes.length > 0
      ? decisionTimes.reduce((a, b) => a + b, 0) / decisionTimes.length
      : 0;

  // ─── Quality scores ───
  const allScores = events.flatMap((e) => e.feedback.scores ?? []);
  const avgQualityScore =
    allScores.length > 0
      ? allScores.reduce((sum, s) => sum + s.score, 0) / allScores.length
      : 0;

  // ─── By server type ───
  const byServerType: AggregateStats["byServerType"] = {};
  for (const event of decisions) {
    const serverType = event.mcpServerType ?? "unknown";
    if (!byServerType[serverType]) {
      byServerType[serverType] = {
        approved: 0,
        rejected: 0,
        needsWork: 0,
        total: 0,
        rate: 0,
      };
    }
    const entry = byServerType[serverType]!;
    entry.total++;
    const d = event.feedback.decision?.decision;
    if (d === "approved") entry.approved++;
    else if (d === "rejected") entry.rejected++;
    else if (d === "needs_work") entry.needsWork++;
  }
  for (const entry of Object.values(byServerType)) {
    entry.rate = entry.total > 0 ? entry.approved / entry.total : 0;
  }

  // ─── By dimension ───
  const byDimension: AggregateStats["byDimension"] = {};
  const dimensionScores = new Map<string, number[]>();

  for (const score of allScores) {
    if (!dimensionScores.has(score.dimension)) {
      dimensionScores.set(score.dimension, []);
    }
    dimensionScores.get(score.dimension)!.push(score.score);
  }

  for (const [dim, scores] of dimensionScores) {
    const sorted = [...scores].sort((a, b) => a - b);
    const avg = scores.reduce((a, b) => a + b, 0) / scores.length;

    // Compute trend: compare first half avg to second half avg
    const mid = Math.floor(scores.length / 2);
    const firstHalf = scores.slice(0, mid);
    const secondHalf = scores.slice(mid);
    const firstAvg =
      firstHalf.length > 0
        ? firstHalf.reduce((a, b) => a + b, 0) / firstHalf.length
        : avg;
    const secondAvg =
      secondHalf.length > 0
        ? secondHalf.reduce((a, b) => a + b, 0) / secondHalf.length
        : avg;

    byDimension[dim] = {
      avg: Math.round(avg * 100) / 100,
      min: sorted[0]!,
      max: sorted[sorted.length - 1]!,
      count: scores.length,
      trend: Math.round((secondAvg - firstAvg) * 100) / 100,
    };
  }

  // ─── By week ───
  const weekBuckets = new Map<
    string,
    { events: EnrichedFeedbackEvent[]; scores: number[]; times: number[] }
  >();

  for (const event of events) {
    const d = new Date(event.timestamp);
    const weekStart = getWeekStart(d);
    if (!weekBuckets.has(weekStart)) {
      weekBuckets.set(weekStart, { events: [], scores: [], times: [] });
    }
    const bucket = weekBuckets.get(weekStart)!;
    bucket.events.push(event);
    for (const s of event.feedback.scores ?? []) {
      bucket.scores.push(s.score);
    }
    if (event.meta.timeToDecisionMs > 0) {
      bucket.times.push(event.meta.timeToDecisionMs);
    }
  }

  const byWeek: WeeklyStats[] = [...weekBuckets.entries()]
    .sort(([a], [b]) => a.localeCompare(b))
    .map(([weekStart, bucket]) => {
      const weekDecisions = bucket.events.filter((e) => e.feedback.decision);
      const weekApproved = weekDecisions.filter(
        (e) => e.feedback.decision?.decision === "approved"
      ).length;
      return {
        weekStart,
        eventCount: bucket.events.length,
        approvalRate:
          weekDecisions.length > 0 ? weekApproved / weekDecisions.length : 0,
        avgScore:
          bucket.scores.length > 0
            ? bucket.scores.reduce((a, b) => a + b, 0) / bucket.scores.length
            : 0,
        avgTimeToDecisionMs:
          bucket.times.length > 0
            ? bucket.times.reduce((a, b) => a + b, 0) / bucket.times.length
            : 0,
      };
    });

  // ─── Volume by day ───
  const volumeByDay: Record<string, number> = {};
  for (const event of events) {
    const day = event.timestamp.split("T")[0]!;
    volumeByDay[day] = (volumeByDay[day] ?? 0) + 1;
  }

  return {
    totalEvents: events.length,
    totalDecisions: decisions.length,
    approvalRate: decisions.length > 0 ? approved / decisions.length : 0,
    rejectionRate: decisions.length > 0 ? rejected / decisions.length : 0,
    needsWorkRate: decisions.length > 0 ? needsWork / decisions.length : 0,
    avgTimeToDecisionMs,
    avgQualityScore: Math.round(avgQualityScore * 100) / 100,
    byServerType,
    byDimension,
    byWeek,
    volumeByDay,
  };
}

function getWeekStart(date: Date): string {
  const d = new Date(date);
  const day = d.getDay();
  const diff = d.getDate() - day + (day === 0 ? -6 : 1); // Monday
  d.setDate(diff);
  return d.toISOString().split("T")[0]!;
}
