/**
 * Behavioral Signal Analysis
 *
 * Analyzes implicit behavioral signals from Jake's review patterns:
 * - Response time patterns (time of day, fatigue)
 * - Engagement depth (scroll depth, fields modified)
 * - Hesitation patterns
 * - Session fatigue detection
 * - Device and context patterns
 */

import type {
  EnrichedFeedbackEvent,
  Anomaly,
  TimeOfDay,
} from "../types.js";

// ═══════════════════════════════════════════════════════════════
// BEHAVIORAL ANALYSIS
// ═══════════════════════════════════════════════════════════════

export interface BehavioralResults {
  /** Response time patterns by time of day */
  timePatterns: Record<
    TimeOfDay,
    {
      avgResponseTimeMs: number;
      avgFeedbackQuality: number;
      count: number;
    }
  >;
  /** Session fatigue analysis */
  fatigueAnalysis: {
    avgQualityDropAfterN: number; // After N reviews, quality drops
    maxProductiveSessionLength: number;
    avgReviewsPerSession: number;
  };
  /** Engagement depth analysis */
  engagementPatterns: {
    avgScrollDepth: number;
    avgFieldsModified: number;
    avgTotalInteractions: number;
    highEngagementCorrelatesWithRejection: boolean;
  };
  /** Detected anomalies */
  anomalies: Anomaly[];
}

/**
 * Analyze implicit behavioral signals from feedback events.
 */
export async function analyzeBehavioral(
  events: EnrichedFeedbackEvent[]
): Promise<BehavioralResults> {
  return {
    timePatterns: analyzeTimePatterns(events),
    fatigueAnalysis: analyzeFatigue(events),
    engagementPatterns: analyzeEngagement(events),
    anomalies: detectBehavioralAnomalies(events),
  };
}

// ═══════════════════════════════════════════════════════════════
// TIME-OF-DAY PATTERNS
// ═══════════════════════════════════════════════════════════════

function analyzeTimePatterns(
  events: EnrichedFeedbackEvent[]
): BehavioralResults["timePatterns"] {
  const buckets: Record<
    TimeOfDay,
    { times: number[]; qualities: number[]; count: number }
  > = {
    morning: { times: [], qualities: [], count: 0 },
    afternoon: { times: [], qualities: [], count: 0 },
    evening: { times: [], qualities: [], count: 0 },
    night: { times: [], qualities: [], count: 0 },
  };

  for (const event of events) {
    const timeOfDay = event.meta.timeOfDay ?? categorizeHour(event.timestamp);
    const bucket = buckets[timeOfDay];
    bucket.times.push(event.meta.timeToDecisionMs);
    bucket.count++;

    // Quality proxy: number of fields modified + scroll depth
    const quality =
      (event.meta.fieldsModified?.length ?? 0) * 0.3 +
      (event.meta.scrollDepth ?? 0.5) * 0.7;
    bucket.qualities.push(quality);
  }

  const result: BehavioralResults["timePatterns"] = {} as any;
  for (const [tod, bucket] of Object.entries(buckets)) {
    result[tod as TimeOfDay] = {
      avgResponseTimeMs:
        bucket.times.length > 0
          ? bucket.times.reduce((a, b) => a + b, 0) / bucket.times.length
          : 0,
      avgFeedbackQuality:
        bucket.qualities.length > 0
          ? bucket.qualities.reduce((a, b) => a + b, 0) /
            bucket.qualities.length
          : 0,
      count: bucket.count,
    };
  }

  return result;
}

// ═══════════════════════════════════════════════════════════════
// SESSION FATIGUE ANALYSIS
// ═══════════════════════════════════════════════════════════════

function analyzeFatigue(
  events: EnrichedFeedbackEvent[]
): BehavioralResults["fatigueAnalysis"] {
  // Group events by session
  const sessions = new Map<string, EnrichedFeedbackEvent[]>();
  for (const event of events) {
    if (!sessions.has(event.sessionId)) {
      sessions.set(event.sessionId, []);
    }
    sessions.get(event.sessionId)!.push(event);
  }

  // Analyze quality drop within sessions
  let qualityDropAfterN = 0;
  let qualityDropCount = 0;

  for (const [, sessionEvents] of sessions) {
    if (sessionEvents.length < 3) continue;

    // Sort by timestamp within session
    const sorted = [...sessionEvents].sort(
      (a, b) =>
        new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
    );

    // Find where quality starts dropping
    for (let i = 1; i < sorted.length; i++) {
      const prevFields = sorted[i - 1]!.meta.fieldsModified?.length ?? 0;
      const currFields = sorted[i]!.meta.fieldsModified?.length ?? 0;
      const prevTime = sorted[i - 1]!.meta.timeToDecisionMs;
      const currTime = sorted[i]!.meta.timeToDecisionMs;

      // Quality drop: fewer fields modified AND faster decisions (rushed)
      if (currFields < prevFields && currTime < prevTime * 0.7) {
        qualityDropAfterN += i;
        qualityDropCount++;
        break;
      }
    }
  }

  const sessionSizes = [...sessions.values()].map((s) => s.length);
  const avgSessionLength =
    sessionSizes.length > 0
      ? sessionSizes.reduce((a, b) => a + b, 0) / sessionSizes.length
      : 0;

  return {
    avgQualityDropAfterN:
      qualityDropCount > 0
        ? Math.round(qualityDropAfterN / qualityDropCount)
        : 10,
    maxProductiveSessionLength:
      qualityDropCount > 0
        ? Math.round(qualityDropAfterN / qualityDropCount)
        : 10,
    avgReviewsPerSession: Math.round(avgSessionLength * 10) / 10,
  };
}

// ═══════════════════════════════════════════════════════════════
// ENGAGEMENT ANALYSIS
// ═══════════════════════════════════════════════════════════════

function analyzeEngagement(
  events: EnrichedFeedbackEvent[]
): BehavioralResults["engagementPatterns"] {
  if (events.length === 0) {
    return {
      avgScrollDepth: 0,
      avgFieldsModified: 0,
      avgTotalInteractions: 0,
      highEngagementCorrelatesWithRejection: false,
    };
  }

  const scrollDepths = events
    .map((e) => e.meta.scrollDepth)
    .filter((d): d is number => d !== undefined);
  const fieldsModified = events.map(
    (e) => e.meta.fieldsModified?.length ?? 0
  );
  const totalInteractions = events.map(
    (e) => e.meta.totalInteractions ?? 0
  );

  // Check if high engagement correlates with rejection
  const highEngagement = events.filter(
    (e) =>
      (e.meta.scrollDepth ?? 0) > 0.7 &&
      (e.meta.fieldsModified?.length ?? 0) > 3
  );
  const highEngagementRejections = highEngagement.filter(
    (e) => e.feedback.decision?.decision === "rejected"
  );
  const highEngagementRejectionRate =
    highEngagement.length > 0
      ? highEngagementRejections.length / highEngagement.length
      : 0;

  const overallRejectionRate =
    events.filter((e) => e.feedback.decision?.decision === "rejected").length /
    events.length;

  return {
    avgScrollDepth:
      scrollDepths.length > 0
        ? Math.round(
            (scrollDepths.reduce((a, b) => a + b, 0) / scrollDepths.length) *
              100
          ) / 100
        : 0,
    avgFieldsModified:
      Math.round(
        (fieldsModified.reduce((a, b) => a + b, 0) / fieldsModified.length) *
          10
      ) / 10,
    avgTotalInteractions:
      Math.round(
        (totalInteractions.reduce((a, b) => a + b, 0) /
          totalInteractions.length) *
          10
      ) / 10,
    highEngagementCorrelatesWithRejection:
      highEngagementRejectionRate > overallRejectionRate * 1.5,
  };
}

// ═══════════════════════════════════════════════════════════════
// BEHAVIORAL ANOMALY DETECTION
// ═══════════════════════════════════════════════════════════════

function detectBehavioralAnomalies(
  events: EnrichedFeedbackEvent[]
): Anomaly[] {
  const anomalies: Anomaly[] = [];

  if (events.length < 5) return anomalies;

  // Check for fatigue — recent events have much faster decision times
  const recentEvents = events.slice(-5);
  const olderEvents = events.slice(0, -5);

  if (olderEvents.length > 0) {
    const recentAvgTime =
      recentEvents.reduce((sum, e) => sum + e.meta.timeToDecisionMs, 0) /
      recentEvents.length;
    const olderAvgTime =
      olderEvents.reduce((sum, e) => sum + e.meta.timeToDecisionMs, 0) /
      olderEvents.length;

    // If recent decisions are 3x faster, might be fatigue
    if (recentAvgTime < olderAvgTime * 0.33 && olderAvgTime > 10000) {
      anomalies.push({
        type: "calibration_drift",
        description: `Decision speed increased dramatically — possible review fatigue (avg ${Math.round(recentAvgTime / 1000)}s vs ${Math.round(olderAvgTime / 1000)}s)`,
        severity: "warning",
        data: { recentAvgMs: recentAvgTime, olderAvgMs: olderAvgTime },
        detectedAt: new Date().toISOString(),
      });
    }
  }

  return anomalies;
}

// ═══════════════════════════════════════════════════════════════
// HELPERS
// ═══════════════════════════════════════════════════════════════

function categorizeHour(isoTimestamp: string): TimeOfDay {
  const hour = new Date(isoTimestamp).getHours();
  if (hour >= 6 && hour < 12) return "morning";
  if (hour >= 12 && hour < 17) return "afternoon";
  if (hour >= 17 && hour < 21) return "evening";
  return "night";
}
