/**
 * Data Sheriff Agent V2 - Autonomous Sports Query Resolution
 * Uses SmartResolver to dynamically discover data from database
 */

import { smartResolver } from './SmartResolver';
import {
  DataSheriffResponse,
  DataSheriffConfig,
  ValidationResult,
  QueryCorrection,
  EnrichedContext,
  InferredData,
  DeconstructedQuery,
  ResolvedEntity,
  PlayerEntity,
  TeamEntity,
  DateEntity,
  IntentClassification,
  QueryIntent,
  CoverageReport,
} from './types';

// Prisma client
let sportsPrisma: any = null;

async function getSportsPrisma() {
  if (!sportsPrisma) {
    try {
      const sportsMod = require('../../../prisma_sports/generated/sports-client');
      sportsPrisma = new sportsMod.PrismaClient();
    } catch {
      try {
        const { PrismaClient } = await import('@prisma/client');
        sportsPrisma = new PrismaClient();
      } catch {
        return null;
      }
    }
  }
  return sportsPrisma;
}

const DEFAULT_CONFIG: DataSheriffConfig = {
  minConfidenceThreshold: 0.5,
  enableFuzzyMatching: true,
  maxFuzzyDistance: 3,
  enableInference: true,
  inferenceConfidenceThreshold: 0.6,
  maxResponseTimeMs: 3000,
  enableCaching: true,
  cacheTTLSeconds: 300,
};

// Intent 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,
  ],
  strategy_backtesting: [
    /backtest|test\s+strategy|simulate|what\s+if|would\s+have/i,
    /betting\s+strategy|system|model|roi|profit/i,
  ],
  player_comparison: [
    /compare|vs\.?\s+|versus|better\s+than|worse\s+than/i,
    /who\s+(is|has|was)\s+(better|more)/i,
  ],
  team_comparison: [
    /which\s+team|team\s+comparison|matchup\s+history|rivalry/i,
  ],
  future_prediction: [
    /will|predict|projection|expected|tonight|tomorrow|upcoming/i,
    /should\s+i\s+bet|pick|who\s+wins/i,
  ],
  odds_lookup: [
    /odds|line|spread|moneyline|ml|point\s+spread|vig|juice/i,
  ],
  injury_check: [
    /injur|hurt|out|questionable|doubtful|status|available|playing/i,
  ],
  general_knowledge: [
    /who\s+is|what\s+is|when|where|why|tell\s+me|explain/i,
  ],
  prop_lookup: [
    /prop|player\s+prop|over\s+\d|under\s+\d|pts\s+o|reb\s+o|ast\s+o/i,
  ],
  trend_analysis: [
    /trend|pattern|streak|consecutive|last\s+\d+\s+games|ats\s+record|cover/i,
  ],
};

export class DataSheriffAgentV2 {
  private config: DataSheriffConfig;
  private cache: Map<string, { data: any; expiry: number }> = new Map();

  constructor(config: Partial<DataSheriffConfig> = {}) {
    this.config = { ...DEFAULT_CONFIG, ...config };
  }

  /**
   * Main entry point - process a user query autonomously
   */
  async processQuery(query: string): Promise<DataSheriffResponse> {
    const startTime = Date.now();

    try {
      // Check cache
      const cacheKey = this.getCacheKey(query);
      if (this.config.enableCaching) {
        const cached = this.getFromCache(cacheKey);
        if (cached) {
          return { ...cached, processingTimeMs: Date.now() - startTime };
        }
      }

      // Use SmartResolver to build context
      const context = await smartResolver.buildContext(query);

      // Build deconstructed query
      const deconstructed = await this.deconstructQuery(query, context);

      // Validate and enrich
      const validation = await this.validate(deconstructed, context);

      // Build enhanced query
      const enhancedQuery = this.buildEnhancedQuery(deconstructed, validation, context);

      // Build context for LLM
      const contextForLLM = this.buildLLMContext(deconstructed, validation, context);

      const response: DataSheriffResponse = {
        success: true,
        deconstructedQuery: deconstructed,
        validation,
        enhancedQuery,
        contextForLLM,
        processingTimeMs: Date.now() - startTime,
        metadata: {
          entitiesResolved: deconstructed.entities.length,
          correctionsApplied: validation.corrections.filter(c => c.applied).length,
          coverageScore: validation.enrichedContext.coverage.overall,
          confidenceScore: this.calculateConfidence(deconstructed, validation),
        },
      };

      if (this.config.enableCaching) {
        this.setCache(cacheKey, response);
      }

      return response;
    } catch (error) {
      console.error('[DataSheriffV2] Error:', error);
      return this.createErrorResponse(query, error, startTime);
    }
  }

  /**
   * Deconstruct query using smart context
   */
  private async deconstructQuery(
    query: string,
    context: Awaited<ReturnType<typeof smartResolver.buildContext>>
  ): Promise<DeconstructedQuery> {
    const entities: ResolvedEntity[] = [];

    // Add resolved players
    for (const player of context.players) {
      entities.push({
        type: 'player',
        raw: player.name,
        canonical: player.name,
        id: player.id?.toString(),
        confidence: player.confidence,
        alternatives: player.alternatives?.map((a: any) => ({
          value: a.name,
          confidence: 0.7,
        })),
        metadata: {
          team: player.team,
          position: player.position,
          league: player.league,
        },
      } as PlayerEntity);
    }

    // Add resolved teams
    for (const team of context.teams) {
      entities.push({
        type: 'team',
        raw: team.name,
        canonical: team.name,
        confidence: team.confidence,
        alternatives: team.alternatives?.map((a: any) => ({
          value: a.name,
          confidence: 0.6,
        })),
        metadata: {
          league: team.league,
          abbreviation: team.abbreviation,
        },
      } as TeamEntity);
    }

    // Add timeframe
    if (context.timeframe) {
      entities.push({
        type: 'date',
        raw: 'detected timeframe',
        canonical: `${context.timeframe.start.toISOString().split('T')[0]} to ${context.timeframe.end.toISOString().split('T')[0]}`,
        startDate: context.timeframe.start,
        endDate: context.timeframe.end,
        isRange: true,
        confidence: 0.9,
      } as DateEntity);
    }

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

    return {
      originalQuery: query,
      normalizedQuery: this.normalizeQuery(query),
      entities,
      intent,
      timeframe: context.timeframe ? {
        type: 'date',
        raw: 'timeframe',
        canonical: 'detected',
        startDate: context.timeframe.start,
        endDate: context.timeframe.end,
        isRange: true,
        confidence: 0.9,
      } : undefined,
      league: context.league || undefined,
    };
  }

  /**
   * Classify query intent
   */
  private classifyIntent(query: string): IntentClassification {
    const scores: Record<QueryIntent, number> = {} as any;

    for (const intent of Object.keys(INTENT_PATTERNS) as QueryIntent[]) {
      scores[intent] = 0;
      for (const pattern of INTENT_PATTERNS[intent]) {
        if (pattern.test(query)) {
          scores[intent]++;
        }
      }
    }

    const sorted = Object.entries(scores).sort(([, a], [, b]) => b - a);
    const primary = (sorted[0]?.[0] || 'general_knowledge') as QueryIntent;
    const primaryScore = sorted[0]?.[1] || 0;

    return {
      primary,
      confidence: Math.min(primaryScore / 3, 1),
      secondary: sorted[1]?.[1] > 0 ? sorted[1][0] as QueryIntent : undefined,
      secondaryConfidence: sorted[1]?.[1] > 0 ? sorted[1][1] / 3 : undefined,
    };
  }

  /**
   * Normalize query
   */
  private normalizeQuery(query: string): string {
    return query
      .trim()
      .replace(/\s+/g, ' ')
      .replace(/\?+$/, '');
  }

  /**
   * Validate the deconstructed query
   */
  private async validate(
    deconstructed: DeconstructedQuery,
    context: Awaited<ReturnType<typeof smartResolver.buildContext>>
  ): Promise<ValidationResult> {
    const corrections: QueryCorrection[] = [];
    const warnings: string[] = [];
    const dataQualityFlags: string[] = [];

    // Check entity confidence and ambiguity
    for (const entity of deconstructed.entities) {
      if (entity.confidence < this.config.minConfidenceThreshold) {
        warnings.push(`Low confidence for "${entity.raw}" (${Math.round(entity.confidence * 100)}%)`);
        dataQualityFlags.push('low_confidence_entity');
      }

      // Check for alternatives
      if (entity.alternatives && entity.alternatives.length > 0 && entity.confidence < 0.9) {
        const alt = entity.alternatives[0];
        warnings.push(`"${entity.raw}" might also refer to "${alt.value}"`);
      }
    }

    // Check for ambiguous player names that need clarification
    for (const player of context.players) {
      if (player.isAmbiguous && player.ambiguityMessage) {
        warnings.push(player.ambiguityMessage);
        dataQualityFlags.push('ambiguous_player_name');
      }
    }

    // Check for unresolved/unknown entities (fake names, typos)
    if (context.unresolvedNames && context.unresolvedNames.length > 0) {
      for (const name of context.unresolvedNames) {
        warnings.push(`Could not find "${name}" in our sports database. This may be a misspelling or an unknown entity.`);
        dataQualityFlags.push('unknown_entity');
      }
    }

    // Check for player injuries
    if (context.players.length > 0) {
      try {
        const prisma = await getSportsPrisma();
        for (const player of context.players) {
          // Build where clause - require league match to avoid cross-sport false positives
          const whereClause: any = {
            status: { not: 'Healthy' },
          };

          // Add league filter if available (critical to avoid matching NHL Fox for NBA Fox)
          const playerLeague = player.league?.toLowerCase();
          if (playerLeague) {
            whereClause.league = playerLeague;
          }

          // Try full name match first, then fall back to last name
          // Full name is more accurate but last name helps with name variations
          const fullNameMatch = await prisma.playerInjury.findFirst({
            where: {
              ...whereClause,
              playerName: { contains: player.name, mode: 'insensitive' },
            },
            orderBy: { updatedAt: 'desc' },
          });

          let injury = fullNameMatch;

          // Only fall back to last name if no full name match AND we have league filter
          if (!injury && playerLeague) {
            const lastName = player.name.split(' ').pop();
            if (lastName && lastName.length > 2) {
              injury = await prisma.playerInjury.findFirst({
                where: {
                  ...whereClause,
                  playerName: { contains: lastName, mode: 'insensitive' },
                },
                orderBy: { updatedAt: 'desc' },
              });
            }
          }

          if (injury) {
            warnings.push(`⚠️ ${player.name} is currently ${injury.status}${injury.injuryType ? ` (${injury.injuryType})` : ''}`);
            dataQualityFlags.push('player_injury');
          } else {
            // IMPORTANT: Explicitly note when NO injury record exists to prevent LLM hallucination
            warnings.push(`✅ ${player.name}: No injury reported in SportsDB (active/healthy)`);
          }
        }
      } catch {
        // Injury check failed
      }
    }

    // Assess coverage
    const coverage = await this.assessCoverage(context);

    if (coverage.overall < 0.5) {
      warnings.push(`Data coverage is limited (${Math.round(coverage.overall * 100)}%)`);
      dataQualityFlags.push('low_coverage');
    }

    // Infer missing data
    const inferredData = this.config.enableInference
      ? await this.inferData(context)
      : [];

    const enrichedContext: EnrichedContext = {
      resolvedEntities: deconstructed.entities,
      coverage,
      relatedData: {
        recentGames: context.recentGames,
        playerStats: context.playerStats,
      },
      dataQualityFlags,
      inferredData: inferredData.length > 0 ? inferredData : undefined,
    };

    const isValid = deconstructed.intent.confidence > 0.2 && coverage.overall > 0.2;
    const confidence = this.calculateConfidence(deconstructed, { isValid, confidence: 0, corrections, warnings, enrichedContext });

    return {
      isValid,
      confidence,
      corrections,
      warnings,
      enrichedContext,
    };
  }

  /**
   * Assess data coverage
   */
  private async assessCoverage(
    context: Awaited<ReturnType<typeof smartResolver.buildContext>>
  ): Promise<CoverageReport> {
    const gaps: any[] = [];

    // Score based on what we found
    let playerStats = context.playerStats.length > 0 ? 0.9 : 0.3;
    let gameData = context.recentGames.length > 0 ? 0.9 : 0.4;
    let bettingLines = 0.5; // Default
    let props = 0.3; // Usually lower
    let injuries = 0.5;

    // Check betting lines in games
    if (context.recentGames.length > 0) {
      const gamesWithOdds = context.recentGames.filter((g: any) => g.moneylineHome || g.spreadHome);
      bettingLines = gamesWithOdds.length / context.recentGames.length;
    }

    // Calculate overall
    const overall = (playerStats * 0.3 + gameData * 0.25 + bettingLines * 0.2 + props * 0.15 + injuries * 0.1);

    return {
      overall,
      breakdown: { playerStats, gameData, bettingLines, props, injuries },
      gaps,
      suggestions: overall < 0.7 ? ['Some data may be incomplete'] : [],
    };
  }

  /**
   * Infer missing data
   */
  private async inferData(
    context: Awaited<ReturnType<typeof smartResolver.buildContext>>
  ): Promise<InferredData[]> {
    const inferred: InferredData[] = [];

    if (context.playerStats.length > 0) {
      // Group stats by key
      const statGroups: Record<string, number[]> = {};
      for (const stat of context.playerStats) {
        if (!statGroups[stat.statKey]) statGroups[stat.statKey] = [];
        statGroups[stat.statKey].push(stat.value);
      }

      // Calculate averages
      for (const [key, values] of Object.entries(statGroups)) {
        const avg = values.reduce((a, b) => a + b, 0) / values.length;
        inferred.push({
          field: `avg_${key}`,
          value: Math.round(avg * 10) / 10,
          basis: `Average of last ${values.length} games`,
          confidence: values.length >= 5 ? 0.85 : 0.65,
          isActual: false,
        });
      }
    }

    return inferred;
  }

  /**
   * Build enhanced query
   */
  private buildEnhancedQuery(
    deconstructed: DeconstructedQuery,
    validation: ValidationResult,
    context: Awaited<ReturnType<typeof smartResolver.buildContext>>
  ): string {
    let enhanced = deconstructed.normalizedQuery;

    // Add league prefix if detected
    if (context.league) {
      enhanced = `[${context.league.toUpperCase()}] ${enhanced}`;
    }

    // Replace informal names with canonical names
    for (const entity of deconstructed.entities) {
      if (entity.confidence > 0.8 && entity.raw !== entity.canonical) {
        enhanced = enhanced.replace(new RegExp(entity.raw, 'gi'), entity.canonical);
      }
    }

    return enhanced;
  }

  /**
   * Build context for LLM
   */
  private buildLLMContext(
    deconstructed: DeconstructedQuery,
    validation: ValidationResult,
    context: Awaited<ReturnType<typeof smartResolver.buildContext>>
  ): string {
    const sections: string[] = [];

    sections.push('=== DATA SHERIFF CONTEXT ===\n');

    // Query info
    sections.push(`## Query Analysis`);
    sections.push(`Intent: ${deconstructed.intent.primary} (${Math.round(deconstructed.intent.confidence * 100)}%)`);
    if (context.league) sections.push(`League: ${context.league.toUpperCase()}`);
    if (context.requestedGameCount && context.requestedGameCount !== 10) {
      sections.push(`**Requested Game Count: ${context.requestedGameCount} games**`);
    }

    // Timeframe constraint - CRITICAL for preventing hallucination
    if (context.timeframe) {
      const startDate = context.timeframe.start.toLocaleDateString();
      const endDate = context.timeframe.end.toLocaleDateString();
      sections.push(`**TIMEFRAME CONSTRAINT: ${startDate} to ${endDate}**`);
      sections.push(`⚠️ ONLY provide data from this timeframe. Do NOT include data from other dates.`);
    }

    // HISTORICAL DATA GAP DETECTION
    // If a historical year was requested but we have no data, flag it explicitly
    if (context.historicalYear && context.playerStats.length === 0 && context.recentGames.length === 0) {
      const currentYear = new Date().getFullYear();
      sections.push(`\n## ⚠️ HISTORICAL DATA LIMITATION`);
      sections.push(`**Requested Year: ${context.historicalYear}**`);
      sections.push(`Our database does not contain complete data for ${context.historicalYear}.`);
      sections.push(`Available data primarily covers the ${currentYear - 1}-${currentYear} and ${currentYear}-${currentYear + 1} seasons.`);
      sections.push(`**DO NOT fabricate or guess stats for ${context.historicalYear}.**`);
      sections.push(`Instead, honestly state that historical data for this period is not available.`);
    }

    // STRICT DATE RANGE ENFORCEMENT
    // When a date range is specified, ensure no data leakage from other periods
    if (context.dateRange) {
      const rangeStart = context.dateRange.start.toLocaleDateString();
      const rangeEnd = context.dateRange.end.toLocaleDateString();
      sections.push(`\n## 🔒 STRICT DATE FILTER APPLIED`);
      sections.push(`Query requested data from: ${rangeStart} to ${rangeEnd}`);
      sections.push(`**ONLY stats from this date range are included below.**`);
      sections.push(`Do NOT include any data from ${new Date().getFullYear()} unless it falls within this range.`);
    }

    // Unknown entities warning - CRITICAL for preventing hallucination about fake entities
    if (context.unresolvedNames && context.unresolvedNames.length > 0) {
      sections.push(`\n## ⚠️ UNKNOWN ENTITIES DETECTED`);
      sections.push(`The following names could NOT be found in our database:`);
      for (const name of context.unresolvedNames) {
        sections.push(`- "${name}" - NOT FOUND`);
      }
      sections.push(`**DO NOT provide stats or make up data for these unknown entities.**`);
      sections.push(`Instead, state that you could not find this player/entity in the database.`);
    }

    // Resolved entities
    if (deconstructed.entities.length > 0) {
      sections.push('\n## Resolved Entities');
      for (const entity of deconstructed.entities) {
        sections.push(`- ${entity.type}: "${entity.canonical}" (${Math.round(entity.confidence * 100)}% confidence)`);
        if (entity.metadata) {
          const meta = entity.metadata as any;
          if (meta.team) sections.push(`  Team: ${meta.team}`);
          if (meta.position) sections.push(`  Position: ${meta.position}`);
        }
      }
    }

    // Recent games
    if (context.recentGames.length > 0) {
      sections.push('\n## Recent Games Data');
      for (const game of context.recentGames.slice(0, 3)) {
        const date = new Date(game.gameDate).toLocaleDateString();
        const score = game.homeScore !== null ? `${game.homeScore}-${game.awayScore}` : 'TBD';
        sections.push(`- ${date}: ${game.awayTeam} @ ${game.homeTeam} (${score})`);
      }
    }

    // Player stats - show ALL available stats, not just top 5
    if (context.playerStats.length > 0) {
      sections.push('\n## Recent Player Stats');
      const statsByGame: Record<string, any[]> = {};
      // Process more stats to capture all stat types
      for (const stat of context.playerStats.slice(0, 100)) {
        const key = stat.gameKey || stat.gameDate;
        if (!statsByGame[key]) statsByGame[key] = [];
        statsByGame[key].push(stat);
      }

      // Show games with ALL their stats (use requested game count or default to 10)
      const gamesToShow = context.requestedGameCount || 10;
      for (const [gameKey, stats] of Object.entries(statsByGame).slice(0, gamesToShow)) {
        const date = stats[0].gameDate ? new Date(stats[0].gameDate).toLocaleDateString() : gameKey;
        const opponent = stats[0].opponent ? ` vs ${stats[0].opponent}` : '';
        sections.push(`- ${date}${opponent}:`);
        // Show ALL stats for this game, not just 5 - prioritize common ones first
        const priorityOrder = ['points', 'espn_pts', 'rebounds', 'espn_reb', 'assists', 'espn_ast',
                               'minutes', 'espn_min', 'steals', 'espn_stl', 'blocks', 'espn_blk',
                               'turnovers', 'espn_to', 'fouls', 'espn_pf', 'fg_pct', '3pt_pct', 'ft_pct'];
        const sortedStats = [...stats].sort((a, b) => {
          const aIdx = priorityOrder.indexOf(a.statKey) >= 0 ? priorityOrder.indexOf(a.statKey) : 999;
          const bIdx = priorityOrder.indexOf(b.statKey) >= 0 ? priorityOrder.indexOf(b.statKey) : 999;
          return aIdx - bIdx;
        });
        // Show all unique stats (dedupe by statKey)
        const seenKeys = new Set<string>();
        for (const stat of sortedStats) {
          if (!seenKeys.has(stat.statKey)) {
            seenKeys.add(stat.statKey);
            sections.push(`  ${stat.statKey}: ${stat.value}`);
          }
        }
      }
    }

    // Inferred data
    if (validation.enrichedContext.inferredData && validation.enrichedContext.inferredData.length > 0) {
      sections.push('\n## Calculated Averages');
      for (const inf of validation.enrichedContext.inferredData.slice(0, 5)) {
        sections.push(`- ${inf.field}: ${inf.value} (${inf.basis})`);
      }
    }

    // Warnings
    if (validation.warnings.length > 0) {
      sections.push('\n## Important Notes');
      for (const warning of validation.warnings.slice(0, 5)) {
        sections.push(`⚠️ ${warning}`);
      }
    }

    // Coverage
    sections.push(`\n## Data Coverage: ${Math.round(validation.enrichedContext.coverage.overall * 100)}%`);

    // Anti-hallucination instruction
    sections.push('\n## CRITICAL INSTRUCTION');
    sections.push('You MUST use ONLY the data provided above. Do NOT:');
    sections.push('- Invent or guess stats not shown in this context');
    sections.push('- Reference games or props not listed here');
    sections.push('- Use your general knowledge to fill in missing data');
    sections.push('If data is not in this context, say "data not available in SportsDB".');

    sections.push('\n=== END CONTEXT ===');

    // CRITICAL: Tell LLM not to include this context in response
    sections.push("\n## IMPORTANT\nThis DATA SHERIFF context is for your internal use ONLY. Do NOT include it, quote it, or reference it in your response to the user. Just answer their question naturally using this data.");
    return sections.join('\n');
  }

  /**
   * Calculate overall confidence
   */
  private calculateConfidence(
    deconstructed: DeconstructedQuery,
    validation: ValidationResult
  ): number {
    const intentConf = deconstructed.intent.confidence;
    const entityConf = deconstructed.entities.length > 0
      ? deconstructed.entities.reduce((sum, e) => sum + e.confidence, 0) / deconstructed.entities.length
      : 0.5;
    const coverageConf = validation.enrichedContext.coverage.overall;

    return (intentConf * 0.3 + entityConf * 0.4 + coverageConf * 0.3);
  }

  /**
   * Create error response
   */
  private createErrorResponse(query: string, error: any, startTime: number): DataSheriffResponse {
    return {
      success: false,
      deconstructedQuery: {
        originalQuery: query,
        normalizedQuery: query,
        entities: [],
        intent: { primary: 'general_knowledge', confidence: 0 },
      },
      validation: {
        isValid: false,
        confidence: 0,
        corrections: [],
        warnings: [`Processing error: ${error?.message || error}`],
        enrichedContext: {
          resolvedEntities: [],
          coverage: { overall: 0, breakdown: { playerStats: 0, gameData: 0, bettingLines: 0, props: 0, injuries: 0 }, gaps: [], suggestions: [] },
          relatedData: {},
          dataQualityFlags: ['error'],
        },
      },
      enhancedQuery: query,
      contextForLLM: '',
      processingTimeMs: Date.now() - startTime,
      metadata: {
        entitiesResolved: 0,
        correctionsApplied: 0,
        coverageScore: 0,
        confidenceScore: 0,
      },
    };
  }

  // Cache helpers
  private getCacheKey(query: string): string {
    return `sheriff_v2:${query.toLowerCase().trim().replace(/\s+/g, '_').slice(0, 100)}`;
  }

  private getFromCache(key: string): DataSheriffResponse | null {
    const cached = this.cache.get(key);
    if (cached && cached.expiry > Date.now()) return cached.data;
    this.cache.delete(key);
    return null;
  }

  private setCache(key: string, data: DataSheriffResponse): void {
    this.cache.set(key, { data, expiry: Date.now() + this.config.cacheTTLSeconds * 1000 });
  }
}

// Export singleton
export const dataSheriffAgentV2 = new DataSheriffAgentV2();
