/**
 * Stage 2: ENRICH
 *
 * Enriches validated feedback events with:
 * 1. Time-of-day and day-of-week categorization
 * 2. Session fatigue calculation
 * 3. NLP theme extraction from free text
 * 4. Sentiment analysis from free text
 * 5. Actionable item extraction
 * 6. Prediction delta computation
 */

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

// ═══════════════════════════════════════════════════════════════
// SESSION TRACKING — For fatigue calculation
// ═══════════════════════════════════════════════════════════════

const sessionCounts = new Map<string, number>();

function getSessionFatigue(sessionId: string): number {
  const count = sessionCounts.get(sessionId) ?? 0;
  sessionCounts.set(sessionId, count + 1);
  return count;
}

// ═══════════════════════════════════════════════════════════════
// TIME CATEGORIZATION
// ═══════════════════════════════════════════════════════════════

function categorizeTime(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";
}

function getDayOfWeek(isoTimestamp: string): string {
  const days = [
    "sunday",
    "monday",
    "tuesday",
    "wednesday",
    "thursday",
    "friday",
    "saturday",
  ];
  return days[new Date(isoTimestamp).getDay()]!;
}

// ═══════════════════════════════════════════════════════════════
// TEXT ANALYSIS — Keyword-based theme extraction & sentiment
// ═══════════════════════════════════════════════════════════════

/** Common themes and their associated keywords */
const THEME_KEYWORDS: Record<string, string[]> = {
  "error-handling": [
    "error",
    "catch",
    "try",
    "exception",
    "throw",
    "handle",
    "failure",
    "fail",
    "retry",
    "fallback",
  ],
  "input-validation": [
    "validation",
    "validate",
    "input",
    "sanitize",
    "zod",
    "schema",
    "type check",
  ],
  "naming-clarity": [
    "name",
    "naming",
    "variable",
    "function name",
    "descriptive",
    "abbreviation",
    "unclear",
  ],
  "test-coverage": [
    "test",
    "coverage",
    "edge case",
    "unit test",
    "assertion",
    "mock",
    "spec",
  ],
  "documentation": [
    "doc",
    "documentation",
    "comment",
    "jsdoc",
    "readme",
    "explain",
  ],
  "code-structure": [
    "architecture",
    "structure",
    "separation",
    "concern",
    "modular",
    "coupling",
    "cohesion",
  ],
  "performance": [
    "performance",
    "speed",
    "slow",
    "fast",
    "efficient",
    "optimize",
    "cache",
    "memory",
  ],
  "security": [
    "security",
    "auth",
    "token",
    "secret",
    "vulnerability",
    "injection",
    "xss",
    "csrf",
  ],
  "type-safety": [
    "typescript",
    "type",
    "any",
    "unknown",
    "generic",
    "strict",
    "typing",
  ],
  "rate-limiting": [
    "rate limit",
    "throttle",
    "backoff",
    "exponential",
    "retry",
    "429",
  ],
  "builder-pattern": ["builder", "pattern", "config", "configuration", "fluent"],
  "clean-code": [
    "clean",
    "readable",
    "maintainable",
    "elegant",
    "simple",
    "clarity",
  ],
};

/** Positive and negative sentiment indicators */
const POSITIVE_WORDS = [
  "great",
  "excellent",
  "nice",
  "good",
  "love",
  "perfect",
  "clean",
  "solid",
  "well done",
  "impressive",
  "beautiful",
  "elegant",
  "thorough",
  "comprehensive",
  "fantastic",
];

const NEGATIVE_WORDS = [
  "bad",
  "poor",
  "missing",
  "broken",
  "wrong",
  "ugly",
  "messy",
  "confusing",
  "incomplete",
  "error",
  "bug",
  "fail",
  "hack",
  "terrible",
  "awful",
  "sloppy",
];

function extractThemes(text: string): string[] {
  const lower = text.toLowerCase();
  const themes: string[] = [];

  for (const [theme, keywords] of Object.entries(THEME_KEYWORDS)) {
    for (const keyword of keywords) {
      if (lower.includes(keyword)) {
        themes.push(theme);
        break; // One match per theme is enough
      }
    }
  }

  return [...new Set(themes)];
}

function analyzeSentiment(text: string): number {
  const lower = text.toLowerCase();
  let score = 0;
  let count = 0;

  for (const word of POSITIVE_WORDS) {
    if (lower.includes(word)) {
      score += 1;
      count++;
    }
  }

  for (const word of NEGATIVE_WORDS) {
    if (lower.includes(word)) {
      score -= 1;
      count++;
    }
  }

  if (count === 0) return 0;
  return Math.max(-1, Math.min(1, score / count));
}

function extractActionItems(text: string): string[] {
  const items: string[] = [];
  const lower = text.toLowerCase();

  // Pattern: "should ...", "need to ...", "must ...", "add ...", "fix ...", "missing ..."
  const actionPatterns = [
    /should\s+(.+?)(?:\.|,|$)/gi,
    /need(?:s)?\s+to\s+(.+?)(?:\.|,|$)/gi,
    /must\s+(.+?)(?:\.|,|$)/gi,
    /(?:please\s+)?add\s+(.+?)(?:\.|,|$)/gi,
    /(?:please\s+)?fix\s+(.+?)(?:\.|,|$)/gi,
    /missing\s+(.+?)(?:\.|,|$)/gi,
  ];

  for (const pattern of actionPatterns) {
    let match;
    while ((match = pattern.exec(text)) !== null) {
      const item = match[1]?.trim();
      if (item && item.length > 5 && item.length < 200) {
        items.push(item);
      }
    }
  }

  return [...new Set(items)];
}

// ═══════════════════════════════════════════════════════════════
// MAIN ENRICHMENT FUNCTION
// ═══════════════════════════════════════════════════════════════

/**
 * Enrich a validated feedback event with derived metadata.
 */
export function enrichFeedback(event: FeedbackEvent): EnrichmentResult {
  try {
    // Collect all free text for analysis
    const allText = [
      event.feedback.freeText?.liked,
      event.feedback.freeText?.disliked,
      event.feedback.freeText?.generalNotes,
      event.feedback.decision?.reason,
      ...(event.feedback.annotations?.map((a) => a.text) ?? []),
    ]
      .filter(Boolean)
      .join(" ");

    // Compute themes, sentiment, action items from text
    const themes = allText.length > 0 ? extractThemes(allText) : undefined;
    const sentiment = allText.length > 0 ? analyzeSentiment(allText) : undefined;
    const actionableItems =
      allText.length > 0 ? extractActionItems(allText) : undefined;

    // Compute prediction delta if Buba made a prediction
    let predictionDelta: number | undefined;
    if (
      event.workProduct.bubaScorePrediction !== undefined &&
      event.feedback.scores &&
      event.feedback.scores.length > 0
    ) {
      const avgActualScore =
        event.feedback.scores.reduce((sum, s) => sum + s.score, 0) /
        event.feedback.scores.length;
      predictionDelta = avgActualScore - event.workProduct.bubaScorePrediction;
    }

    const enriched: EnrichedFeedbackEvent = {
      ...event,
      _enriched: true as const,
      _themes: themes && themes.length > 0 ? themes : undefined,
      _sentiment: sentiment,
      _actionableItems:
        actionableItems && actionableItems.length > 0
          ? actionableItems
          : undefined,
      _predictionDelta: predictionDelta,
      _pipelineContext: event.pipelineStage
        ? {
            stage: event.pipelineStage,
            stagesCompleted: 0, // Would be looked up from pipeline state
            totalDuration: 0,
          }
        : undefined,
      meta: {
        ...event.meta,
        timeOfDay: categorizeTime(event.timestamp),
        dayOfWeek: getDayOfWeek(event.timestamp),
        sessionFatigue: getSessionFatigue(event.sessionId),
        concurrentModals: event.meta.concurrentModals ?? 0,
        extractedThemes: themes,
        sentiment,
        actionableItems:
          actionableItems && actionableItems.length > 0
            ? actionableItems
            : undefined,
        predictionDelta,
      },
    };

    return { success: true, event: enriched };
  } catch (error) {
    return {
      success: false,
      error: `Enrichment failed: ${error instanceof Error ? error.message : String(error)}`,
    };
  }
}

/**
 * Reset session tracking (for testing).
 */
export function resetSessionTracking(): void {
  sessionCounts.clear();
}
