/**
 * Voice Intent Classification
 *
 * Classifies voice commands into structured activity intents
 * for feeding, sleep, diaper tracking with entity extraction.
 */

export enum IntentType {
  FEEDING = 'feeding',
  SLEEP = 'sleep',
  DIAPER = 'diaper',
  UNKNOWN = 'unknown',
}

export enum FeedingType {
  BOTTLE = 'bottle',
  BREAST_LEFT = 'breast_left',
  BREAST_RIGHT = 'breast_right',
  BREAST_BOTH = 'breast_both',
  SOLID = 'solid',
}

export enum SleepType {
  NAP = 'nap',
  NIGHT = 'night',
}

export enum DiaperType {
  WET = 'wet',
  DIRTY = 'dirty',
  BOTH = 'both',
  DRY = 'dry',
}

export interface Entity {
  type: string;
  value: any;
  confidence: number;
  text: string;
}

export interface ClassificationResult {
  intent: IntentType;
  confidence: number;
  subtype?: string;
  entities: Entity[];
  rawText: string;
  structuredData?: FeedingData | SleepData | DiaperData;
}

export interface FeedingData {
  type: FeedingType;
  amount?: number;
  unit?: 'ml' | 'oz' | 'tbsp';
  duration?: number; // minutes
  side?: 'left' | 'right' | 'both';
  startTime?: Date;
  endTime?: Date;
  notes?: string;
}

export interface SleepData {
  type: SleepType;
  startTime?: Date;
  endTime?: Date;
  duration?: number; // minutes
  notes?: string;
}

export interface DiaperData {
  type: DiaperType;
  time?: Date;
  notes?: string;
}

/**
 * Intent classification patterns
 */
const INTENT_PATTERNS = {
  feeding: [
    // Bottle feeding
    /fed\s+(baby|him|her)?\s*(\d+)?\s*(ml|milliliters?|oz|ounces?|tbsp|tablespoons?)?/i,
    /gave\s+(baby|him|her)?\s*(\d+)?\s*(ml|milliliters?|oz|ounces?)?/i,
    /bottle\s*fed/i,
    /(bottle|formula)\s*feeding/i,
    /drank\s*(\d+)?\s*(ml|oz)/i,

    // Breastfeeding
    /breast\s*fed/i,
    /breastfe(e|a)ding/i,
    /nursed/i,
    /nursing/i,
    /(fed|feeding)\s+(from|on)\s+(left|right|both)\s+(breast|side)/i,

    // Solid food
    /ate\s+(solid|food|meal)/i,
    /solid\s+food/i,
    /had\s+(breakfast|lunch|dinner|snack)/i,

    // General feeding
    /feed\s+(start|begin|end)/i,
    /feeding\s+time/i,
  ],

  sleep: [
    /sleep/i,
    /nap/i,
    /sleeping/i,
    /napping/i,
    /fell\s+asleep/i,
    /woke\s+up/i,
    /asleep/i,
    /bedtime/i,
    /put\s+(baby|him|her)\s+(down|to\s+sleep)/i,
  ],

  diaper: [
    /diaper/i,
    /nappy/i,
    /changed\s+(diaper|nappy)/i,
    /diaper\s+change/i,
    /(wet|dirty|soiled|poopy|poop|pee)\s+(diaper|nappy)?/i,
    /bowel\s+movement/i,
    /bm\b/i,
  ],
};

/**
 * Entity extraction patterns
 */
const ENTITY_PATTERNS = {
  // Amounts with units
  amount: [
    /(\d+(?:\.\d+)?)\s*(ml|milliliters?|cc)/gi,
    /(\d+(?:\.\d+)?)\s*(oz|ounces?)/gi,
    /(\d+(?:\.\d+)?)\s*(tbsp|tablespoons?)/gi,
  ],

  // Duration in minutes
  duration: [
    /(\d+)\s*(minutes?|mins?)\b/gi,
    /for\s+(\d+)\s*(minutes?|mins?)/gi,
    /lasted\s+(\d+)\s*(minutes?|mins?)/gi,
  ],

  // Time expressions
  time: [
    /at\s+(\d{1,2}):(\d{2})\s*(am|pm)?/gi,
    /(\d{1,2})\s*(am|pm)/gi,
    /(morning|afternoon|evening|night)/gi,
  ],

  // Relative time
  relativeTime: [
    /(\d+)\s*(hours?|hrs?)\s+ago/gi,
    /(\d+)\s*(minutes?|mins?)\s+ago/gi,
    /just\s+now/gi,
    /a\s+moment\s+ago/gi,
  ],

  // Breast side
  breastSide: [
    /(left|right)\s+(breast|side|boob)/gi,
    /both\s+(breasts?|sides?|boobs?)/gi,
  ],

  // Diaper type
  diaperType: [
    /\b(wet|pee|urine)\b/gi,
    /\b(dirty|poop|poopy|soiled|bowel\s+movement|bm)\b/gi,
    /\b(dry|clean)\b/gi,
  ],

  // Sleep type
  sleepType: [
    /\b(nap|napping)\b/gi,
    /\b(night|bedtime|overnight)\b/gi,
  ],
};

/**
 * Classifies the intent of a voice command
 */
export function classifyIntent(text: string): ClassificationResult {
  const normalizedText = text.toLowerCase().trim();

  // Check each intent type
  let highestConfidence = 0;
  let matchedIntent = IntentType.UNKNOWN;

  for (const [intent, patterns] of Object.entries(INTENT_PATTERNS)) {
    let matchCount = 0;
    for (const pattern of patterns) {
      if (pattern.test(normalizedText)) {
        matchCount++;
      }
    }

    // Calculate confidence based on match count
    const confidence = Math.min(matchCount * 0.4, 1.0);
    if (confidence > highestConfidence) {
      highestConfidence = confidence;
      matchedIntent = intent as IntentType;
    }
  }

  // Extract entities based on intent
  const entities = extractEntities(normalizedText, matchedIntent);

  // Build structured data
  const structuredData = buildStructuredData(matchedIntent, entities, text);

  return {
    intent: matchedIntent,
    confidence: highestConfidence,
    entities,
    rawText: text,
    structuredData,
  };
}

/**
 * Extracts entities from text based on intent
 */
function extractEntities(text: string, intent: IntentType): Entity[] {
  const entities: Entity[] = [];

  // Extract amount
  for (const pattern of ENTITY_PATTERNS.amount) {
    const matches = Array.from(text.matchAll(pattern));
    for (const match of matches) {
      entities.push({
        type: 'amount',
        value: parseFloat(match[1]),
        confidence: 0.9,
        text: match[0],
      });
    }
  }

  // Extract duration
  for (const pattern of ENTITY_PATTERNS.duration) {
    const matches = Array.from(text.matchAll(pattern));
    for (const match of matches) {
      entities.push({
        type: 'duration',
        value: parseInt(match[1]),
        confidence: 0.9,
        text: match[0],
      });
    }
  }

  // Extract time
  for (const pattern of ENTITY_PATTERNS.time) {
    const matches = Array.from(text.matchAll(pattern));
    for (const match of matches) {
      entities.push({
        type: 'time',
        value: match[0],
        confidence: 0.8,
        text: match[0],
      });
    }
  }

  // Extract relative time
  for (const pattern of ENTITY_PATTERNS.relativeTime) {
    const matches = Array.from(text.matchAll(pattern));
    for (const match of matches) {
      entities.push({
        type: 'relativeTime',
        value: match[0],
        confidence: 0.85,
        text: match[0],
      });
    }
  }

  // Intent-specific entities
  if (intent === IntentType.FEEDING) {
    // Extract breast side
    for (const pattern of ENTITY_PATTERNS.breastSide) {
      const matches = Array.from(text.matchAll(pattern));
      for (const match of matches) {
        entities.push({
          type: 'breastSide',
          value: match[1].toLowerCase(),
          confidence: 0.9,
          text: match[0],
        });
      }
    }
  }

  if (intent === IntentType.DIAPER) {
    // Extract diaper type
    for (const pattern of ENTITY_PATTERNS.diaperType) {
      const matches = Array.from(text.matchAll(pattern));
      for (const match of matches) {
        entities.push({
          type: 'diaperType',
          value: match[1].toLowerCase(),
          confidence: 0.9,
          text: match[0],
        });
      }
    }
  }

  if (intent === IntentType.SLEEP) {
    // Extract sleep type
    for (const pattern of ENTITY_PATTERNS.sleepType) {
      const matches = Array.from(text.matchAll(pattern));
      for (const match of matches) {
        entities.push({
          type: 'sleepType',
          value: match[1].toLowerCase(),
          confidence: 0.9,
          text: match[0],
        });
      }
    }
  }

  return entities;
}

/**
 * Builds structured data from entities
 */
function buildStructuredData(
  intent: IntentType,
  entities: Entity[],
  rawText: string
): FeedingData | SleepData | DiaperData | undefined {
  if (intent === IntentType.FEEDING) {
    return buildFeedingData(entities, rawText);
  } else if (intent === IntentType.SLEEP) {
    return buildSleepData(entities, rawText);
  } else if (intent === IntentType.DIAPER) {
    return buildDiaperData(entities, rawText);
  }

  return undefined;
}

/**
 * Builds feeding data from entities
 */
function buildFeedingData(entities: Entity[], rawText: string): FeedingData {
  const data: FeedingData = {
    type: FeedingType.BOTTLE, // Default
  };

  // Determine feeding type from text
  const lowerText = rawText.toLowerCase();
  if (lowerText.includes('breast') || lowerText.includes('nurs')) {
    const breastSide = entities.find(e => e.type === 'breastSide');
    if (breastSide) {
      if (breastSide.value === 'left') data.type = FeedingType.BREAST_LEFT;
      else if (breastSide.value === 'right') data.type = FeedingType.BREAST_RIGHT;
      else if (breastSide.value === 'both') data.type = FeedingType.BREAST_BOTH;
      data.side = breastSide.value;
    } else {
      data.type = FeedingType.BREAST_BOTH; // Default to both if not specified
    }
  } else if (lowerText.includes('solid') || lowerText.includes('food') || lowerText.includes('ate')) {
    data.type = FeedingType.SOLID;
  } else {
    data.type = FeedingType.BOTTLE;
  }

  // Extract amount
  const amountEntity = entities.find(e => e.type === 'amount');
  if (amountEntity) {
    data.amount = amountEntity.value;
    // Determine unit from text
    if (rawText.toLowerCase().includes('ml') || rawText.toLowerCase().includes('milliliter')) {
      data.unit = 'ml';
    } else if (rawText.toLowerCase().includes('oz') || rawText.toLowerCase().includes('ounce')) {
      data.unit = 'oz';
    } else if (rawText.toLowerCase().includes('tbsp') || rawText.toLowerCase().includes('tablespoon')) {
      data.unit = 'tbsp';
    } else {
      data.unit = 'ml'; // Default
    }
  }

  // Extract duration
  const durationEntity = entities.find(e => e.type === 'duration');
  if (durationEntity) {
    data.duration = durationEntity.value;
  }

  // Extract time
  const timeEntity = entities.find(e => e.type === 'time' || e.type === 'relativeTime');
  if (timeEntity) {
    data.startTime = parseTime(timeEntity.value);
  }

  return data;
}

/**
 * Builds sleep data from entities
 */
function buildSleepData(entities: Entity[], rawText: string): SleepData {
  const data: SleepData = {
    type: SleepType.NAP, // Default
  };

  // Determine sleep type
  const sleepTypeEntity = entities.find(e => e.type === 'sleepType');
  if (sleepTypeEntity) {
    if (sleepTypeEntity.value === 'night' || sleepTypeEntity.value === 'bedtime') {
      data.type = SleepType.NIGHT;
    } else {
      data.type = SleepType.NAP;
    }
  }

  // Extract duration
  const durationEntity = entities.find(e => e.type === 'duration');
  if (durationEntity) {
    data.duration = durationEntity.value;
  }

  // Extract time
  const timeEntity = entities.find(e => e.type === 'time' || e.type === 'relativeTime');
  if (timeEntity) {
    data.startTime = parseTime(timeEntity.value);
  }

  return data;
}

/**
 * Builds diaper data from entities
 */
function buildDiaperData(entities: Entity[], rawText: string): DiaperData {
  const data: DiaperData = {
    type: DiaperType.WET, // Default
  };

  // Determine diaper type
  const diaperTypeEntities = entities.filter(e => e.type === 'diaperType');
  const types = diaperTypeEntities.map(e => e.value);

  const hasWet = types.some(t => ['wet', 'pee', 'urine'].includes(t));
  const hasDirty = types.some(t => ['dirty', 'poop', 'poopy', 'soiled', 'bowel', 'bm'].includes(t));
  const hasDry = types.some(t => ['dry', 'clean'].includes(t));

  if (hasWet && hasDirty) {
    data.type = DiaperType.BOTH;
  } else if (hasDirty) {
    data.type = DiaperType.DIRTY;
  } else if (hasDry) {
    data.type = DiaperType.DRY;
  } else {
    data.type = DiaperType.WET;
  }

  // Extract time
  const timeEntity = entities.find(e => e.type === 'time' || e.type === 'relativeTime');
  if (timeEntity) {
    data.time = parseTime(timeEntity.value);
  }

  return data;
}

/**
 * Parses time expressions into Date objects
 */
function parseTime(timeText: string): Date {
  const now = new Date();

  // Handle relative time
  if (timeText.includes('ago')) {
    const hoursMatch = timeText.match(/(\d+)\s*hours?/i);
    const minutesMatch = timeText.match(/(\d+)\s*minutes?/i);

    if (hoursMatch) {
      return new Date(now.getTime() - parseInt(hoursMatch[1]) * 60 * 60 * 1000);
    }
    if (minutesMatch) {
      return new Date(now.getTime() - parseInt(minutesMatch[1]) * 60 * 1000);
    }
  }

  // Handle "just now" or "a moment ago"
  if (timeText.includes('just now') || timeText.includes('moment ago')) {
    return now;
  }

  // Handle specific times (simplified - would need more robust parsing in production)
  const timeMatch = timeText.match(/(\d{1,2}):(\d{2})\s*(am|pm)?/i);
  if (timeMatch) {
    let hours = parseInt(timeMatch[1]);
    const minutes = parseInt(timeMatch[2]);
    const period = timeMatch[3]?.toLowerCase();

    if (period === 'pm' && hours < 12) hours += 12;
    if (period === 'am' && hours === 12) hours = 0;

    const result = new Date(now);
    result.setHours(hours, minutes, 0, 0);
    return result;
  }

  // Default to now
  return now;
}

/**
 * Confidence scoring helper
 */
export function getConfidenceLevel(confidence: number): 'high' | 'medium' | 'low' {
  if (confidence >= 0.8) return 'high';
  if (confidence >= 0.5) return 'medium';
  return 'low';
}

/**
 * Validates classification result
 */
export function validateClassification(result: ClassificationResult): boolean {
  return result.intent !== IntentType.UNKNOWN && result.confidence >= 0.3;
}
