/**
 * Query Deconstructor Service
 * Parses user queries, extracts entities, and classifies intent
 */

import {
  DeconstructedQuery,
  IntentClassification,
  QueryIntent,
  ResolvedEntity,
  DateEntity,
  LEAGUE_SPORT_MAP,
} from './types';
import { EntityResolver, entityResolver } from './EntityResolver';

// Intent classification patterns
const INTENT_PATTERNS: Record<QueryIntent, RegExp[]> = {
  historical_performance: [
    /how\s+did|how\s+many|what\s+were|stats?\s+for|performance|scored?|average/i,
    /last\s+(game|week|month|season|year)/i,
    /against|vs\.?|versus/i,
    /record|history/i,
  ],
  strategy_backtesting: [
    /backtest|test\s+strategy|simulate|what\s+if|would\s+have/i,
    /betting\s+strategy|system|model/i,
    /roi|profit|return|edge/i,
    /home\s+(dogs?|favorites?|underdogs?)|away\s+(dogs?|favorites?)/i,
  ],
  player_comparison: [
    /compare|vs\.?\s+|versus|better\s+than|worse\s+than|or\b/i,
    /who\s+(is|has|was)\s+(better|more|higher|lower)/i,
    /head\s*to\s*head|matchup/i,
  ],
  team_comparison: [
    /which\s+team|team\s+comparison|matchup\s+history/i,
    /rivalry|all\s*-?\s*time\s+record/i,
  ],
  future_prediction: [
    /will|predict|projection|expected|forecast|tonight|tomorrow|upcoming/i,
    /should\s+i\s+bet|pick|who\s+wins/i,
    /over\s*\/?\s*under\s+(\d+\.?\d*)|o\/?u\s+(\d+)/i,
  ],
  odds_lookup: [
    /odds|line|spread|moneyline|ml|point\s+spread/i,
    /vig|juice|betting\s+line/i,
    /open(ing|ed)?\s+line|current\s+line/i,
  ],
  injury_check: [
    /injur(y|ed|ies)|hurt|out|questionable|doubtful|gtd|game\s*time/i,
    /status|available|playing/i,
    /health|return|recovery/i,
  ],
  general_knowledge: [
    /who\s+is|what\s+is|when\s+(did|was|is)|where|why|tell\s+me\s+about/i,
    /explain|define|meaning/i,
  ],
  prop_lookup: [
    /prop|player\s+prop|over\s+(\d+\.?\d*)\s+(points?|rebounds?|assists?)/i,
    /pts\s+o\/?u|reb\s+o\/?u|ast\s+o\/?u/i,
    /total\s+(points?|rebounds?|assists?|yards?|touchdowns?)/i,
  ],
  trend_analysis: [
    /trend|pattern|streak|consecutive|last\s+\d+\s+games/i,
    /ats\s+record|against\s+the\s+spread|cover/i,
    /over\s+percentage|under\s+percentage/i,
  ],
};

// Query expansion templates
const EXPANSION_TEMPLATES: Record<QueryIntent, string[]> = {
  historical_performance: [
    'Game statistics and box score',
    'Comparison to season averages',
    'Performance in similar situations',
  ],
  strategy_backtesting: [
    'Historical win rate',
    'ROI calculation',
    'Sample size and confidence',
    'Risk metrics',
  ],
  player_comparison: [
    'Head-to-head statistics',
    'Career comparison',
    'Recent form comparison',
  ],
  team_comparison: [
    'All-time series record',
    'Recent matchups',
    'Home/away splits',
  ],
  future_prediction: [
    'Current betting lines',
    'Injury report impact',
    'Historical trends in similar matchups',
  ],
  odds_lookup: [
    'Opening vs current line movement',
    'Consensus line',
    'Historical line performance',
  ],
  injury_check: [
    'Current injury status',
    'Expected return timeline',
    'Replacement player impact',
  ],
  general_knowledge: [
    'Key facts and statistics',
    'Recent news and updates',
  ],
  prop_lookup: [
    'Current prop lines',
    'Historical hit rates',
    'Matchup-specific adjustments',
  ],
  trend_analysis: [
    'Recent performance trend',
    'ATS record breakdown',
    'Over/under trends',
  ],
};

export class QueryDeconstructor {
  private entityResolver: EntityResolver;

  constructor() {
    this.entityResolver = entityResolver;
  }

  /**
   * Deconstruct a user query into components
   */
  async deconstruct(query: string): Promise<DeconstructedQuery> {
    // Normalize the query
    const normalizedQuery = this.normalizeQuery(query);

    // Detect league/sport context
    const league = this.entityResolver.detectLeague(query);
    const sport = league ? LEAGUE_SPORT_MAP[league] : null;

    // Extract all entities
    const entities = await this.entityResolver.resolveAllEntities(query, league || undefined);

    // Classify intent
    const intent = this.classifyIntent(query, entities);

    // Extract timeframe
    const timeframe = entities.find(e => e.type === 'date') as DateEntity | undefined;

    // Generate expanded queries
    const expandedQueries = this.expandQuery(query, intent.primary, entities);

    return {
      originalQuery: query,
      normalizedQuery,
      entities,
      intent,
      timeframe,
      league: league || undefined,
      sport: sport || undefined,
      expandedQueries,
    };
  }

  /**
   * Normalize query text
   */
  private normalizeQuery(query: string): string {
    let normalized = query.trim();

    // Expand common abbreviations
    const abbreviations: Record<string, string> = {
      'pts': 'points',
      'reb': 'rebounds',
      'ast': 'assists',
      'stl': 'steals',
      'blk': 'blocks',
      'to': 'turnovers',
      'min': 'minutes',
      'fg': 'field goals',
      '3pt': 'three pointers',
      'ft': 'free throws',
      'ml': 'moneyline',
      'o/u': 'over under',
      'ats': 'against the spread',
      'td': 'touchdown',
      'yds': 'yards',
      'int': 'interception',
      'hr': 'home run',
      'rbi': 'runs batted in',
    };

    for (const [abbrev, full] of Object.entries(abbreviations)) {
      const pattern = new RegExp(`\\b${abbrev}\\b`, 'gi');
      normalized = normalized.replace(pattern, full);
    }

    // Standardize question phrasing
    normalized = normalized
      .replace(/\?+$/, '')
      .replace(/\s+/g, ' ');

    return normalized;
  }

  /**
   * Classify the intent of a query
   */
  private classifyIntent(
    query: string,
    entities: ResolvedEntity[]
  ): IntentClassification {
    const scores: Record<QueryIntent, number> = {
      historical_performance: 0,
      strategy_backtesting: 0,
      player_comparison: 0,
      team_comparison: 0,
      future_prediction: 0,
      odds_lookup: 0,
      injury_check: 0,
      general_knowledge: 0,
      prop_lookup: 0,
      trend_analysis: 0,
    };

    // Score based on pattern matches
    for (const [intent, patterns] of Object.entries(INTENT_PATTERNS)) {
      for (const pattern of patterns) {
        if (pattern.test(query)) {
          scores[intent as QueryIntent] += 1;
        }
      }
    }

    // Adjust scores based on entities present
    const hasPlayers = entities.some(e => e.type === 'player');
    const hasTeams = entities.some(e => e.type === 'team');
    const hasDate = entities.some(e => e.type === 'date');
    const hasMetric = entities.some(e => e.type === 'metric');

    // Historical performance likely if player + date
    if (hasPlayers && hasDate) {
      scores.historical_performance += 0.5;
    }

    // Player comparison if multiple players
    const playerCount = entities.filter(e => e.type === 'player').length;
    if (playerCount >= 2) {
      scores.player_comparison += 1;
    }

    // Team comparison if multiple teams
    const teamCount = entities.filter(e => e.type === 'team').length;
    if (teamCount >= 2) {
      scores.team_comparison += 1;
    }

    // Strategy backtesting if no specific entities but has betting terms
    if (!hasPlayers && !hasTeams && scores.strategy_backtesting > 0) {
      scores.strategy_backtesting += 0.5;
    }

    // Find primary and secondary intents
    const sortedIntents = Object.entries(scores)
      .sort(([, a], [, b]) => b - a)
      .filter(([, score]) => score > 0);

    const primary = sortedIntents[0]?.[0] as QueryIntent || 'general_knowledge';
    const primaryScore = sortedIntents[0]?.[1] || 0;
    const secondary = sortedIntents[1]?.[0] as QueryIntent;
    const secondaryScore = sortedIntents[1]?.[1] || 0;

    // Calculate confidence (normalize by max possible score)
    const maxPossible = Math.max(...Object.values(INTENT_PATTERNS).map(p => p.length)) + 1.5;
    const confidence = Math.min(primaryScore / maxPossible, 1);

    return {
      primary,
      confidence,
      secondary: secondaryScore > 0 ? secondary : undefined,
      secondaryConfidence: secondaryScore > 0 ? secondaryScore / maxPossible : undefined,
    };
  }

  /**
   * Expand query into sub-queries
   */
  private expandQuery(
    query: string,
    intent: QueryIntent,
    entities: ResolvedEntity[]
  ): string[] {
    const templates = EXPANSION_TEMPLATES[intent] || [];
    const expanded: string[] = [];

    // Add base query
    expanded.push(query);

    // Add template-based expansions
    for (const template of templates) {
      expanded.push(template);
    }

    // Add entity-specific expansions
    const players = entities.filter(e => e.type === 'player');
    const teams = entities.filter(e => e.type === 'team');

    for (const player of players.slice(0, 2)) {
      expanded.push(`${player.canonical} recent performance and trends`);
    }

    for (const team of teams.slice(0, 2)) {
      expanded.push(`${team.canonical} recent results and form`);
    }

    return expanded;
  }

  /**
   * Extract specific data needs from query
   */
  extractDataNeeds(deconstructed: DeconstructedQuery): {
    needsPlayerStats: boolean;
    needsGameData: boolean;
    needsBettingLines: boolean;
    needsProps: boolean;
    needsInjuries: boolean;
    specificStats: string[];
  } {
    const { intent, entities, originalQuery } = deconstructed;

    const needsPlayerStats =
      intent.primary === 'historical_performance' ||
      intent.primary === 'player_comparison' ||
      intent.primary === 'prop_lookup' ||
      entities.some(e => e.type === 'player');

    const needsGameData =
      intent.primary === 'historical_performance' ||
      intent.primary === 'team_comparison' ||
      intent.primary === 'trend_analysis' ||
      entities.some(e => e.type === 'team');

    const needsBettingLines =
      intent.primary === 'odds_lookup' ||
      intent.primary === 'strategy_backtesting' ||
      intent.primary === 'trend_analysis' ||
      /spread|moneyline|odds|line/i.test(originalQuery);

    const needsProps =
      intent.primary === 'prop_lookup' ||
      /prop|over\s+\d+|under\s+\d+/i.test(originalQuery);

    const needsInjuries =
      intent.primary === 'injury_check' ||
      intent.primary === 'future_prediction' ||
      /injur|status|available/i.test(originalQuery);

    // Extract specific stats mentioned
    const specificStats: string[] = [];
    const statKeywords = [
      'points', 'rebounds', 'assists', 'steals', 'blocks',
      'minutes', 'field goal', 'three point', 'free throw',
      'passing yards', 'rushing yards', 'touchdowns', 'interceptions',
      'home runs', 'rbi', 'batting average', 'era',
      'goals', 'saves', 'shots',
    ];

    for (const stat of statKeywords) {
      if (originalQuery.toLowerCase().includes(stat)) {
        specificStats.push(stat);
      }
    }

    return {
      needsPlayerStats,
      needsGameData,
      needsBettingLines,
      needsProps,
      needsInjuries,
      specificStats,
    };
  }

  /**
   * Detect potential issues or ambiguities in query
   */
  detectAmbiguities(deconstructed: DeconstructedQuery): string[] {
    const ambiguities: string[] = [];
    const { entities, originalQuery, league } = deconstructed;

    // Check for ambiguous player names
    for (const entity of entities) {
      if (entity.type === 'player' && entity.alternatives && entity.alternatives.length > 0) {
        if (entity.confidence < 0.9) {
          ambiguities.push(
            `"${entity.raw}" could refer to ${entity.canonical} ` +
            `or ${entity.alternatives[0].value} (${Math.round(entity.alternatives[0].confidence * 100)}% match)`
          );
        }
      }
    }

    // Check for ambiguous location references
    if (/\b(la|ny|chi)\b/i.test(originalQuery) && !league) {
      if (/\bla\b/i.test(originalQuery)) {
        ambiguities.push('"LA" could refer to Lakers, Clippers (NBA), Dodgers, Angels (MLB), Rams, Chargers (NFL)');
      }
      if (/\bny\b/i.test(originalQuery)) {
        ambiguities.push('"NY" could refer to Knicks, Nets (NBA), Yankees, Mets (MLB), Giants, Jets (NFL)');
      }
    }

    // Check for missing date context
    const hasDate = entities.some(e => e.type === 'date');
    if (!hasDate && (
      deconstructed.intent.primary === 'historical_performance' ||
      deconstructed.intent.primary === 'odds_lookup'
    )) {
      ambiguities.push('No specific date/timeframe mentioned - using recent data');
    }

    // Check for traded players
    const players = entities.filter(e => e.type === 'player');
    // This would need actual trade data to be accurate
    // For now, just flag if asking about historical data

    return ambiguities;
  }
}

export const queryDeconstructor = new QueryDeconstructor();
