/**
 * Data Sheriff Agent - Main Orchestrator
 * Validates and enriches queries before LLM processing
 */

import {
  DataSheriffResponse,
  DataSheriffConfig,
  ValidationResult,
  QueryCorrection,
  EnrichedContext,
  InferredData,
  DeconstructedQuery,
  ResolvedEntity,
  PlayerEntity,
  DateEntity,
} from './types';
import { entityResolver } from './EntityResolver';
import { coverageChecker } from './CoverageChecker';
import { queryDeconstructor } from './QueryDeconstructor';

// Prisma clients - lazy loaded
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.6,
  enableFuzzyMatching: true,
  maxFuzzyDistance: 3,
  enableInference: true,
  inferenceConfidenceThreshold: 0.7,
  maxResponseTimeMs: 2000,
  enableCaching: true,
  cacheTTLSeconds: 300,
};

export class DataSheriffAgent {
  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
   */
  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,
          };
        }
      }

      // Phase 1: Query Deconstruction
      const deconstructed = await queryDeconstructor.deconstruct(query);

      // Phase 2: Validation Pipeline
      const validation = await this.validateQuery(deconstructed);

      // Phase 3: Build Enhanced Query
      const enhancedQuery = this.buildEnhancedQuery(deconstructed, validation);

      // Phase 4: Build Context for LLM
      const contextForLLM = this.buildLLMContext(deconstructed, validation);

      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.calculateOverallConfidence(deconstructed, validation),
        },
      };

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

      return response;
    } catch (error) {
      console.error('DataSheriffAgent error:', error);
      return {
        success: false,
        deconstructedQuery: {
          originalQuery: query,
          normalizedQuery: query,
          entities: [],
          intent: { primary: 'general_knowledge', confidence: 0 },
        },
        validation: {
          isValid: false,
          confidence: 0,
          corrections: [],
          warnings: [`Error processing query: ${error}`],
          enrichedContext: {
            resolvedEntities: [],
            coverage: {
              overall: 0,
              breakdown: { playerStats: 0, gameData: 0, bettingLines: 0, props: 0, injuries: 0 },
              gaps: [],
              suggestions: [],
            },
            relatedData: {},
            dataQualityFlags: ['processing_error'],
          },
        },
        enhancedQuery: query,
        contextForLLM: '',
        processingTimeMs: Date.now() - startTime,
        metadata: {
          entitiesResolved: 0,
          correctionsApplied: 0,
          coverageScore: 0,
          confidenceScore: 0,
        },
      };
    }
  }

  /**
   * Validate the deconstructed query
   */
  private async validateQuery(deconstructed: DeconstructedQuery): Promise<ValidationResult> {
    const corrections: QueryCorrection[] = [];
    const warnings: string[] = [];
    const dataQualityFlags: string[] = [];

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

      // Check for suggested corrections
      if (entity.alternatives && entity.alternatives.length > 0) {
        const bestAlt = entity.alternatives[0];
        if (bestAlt.confidence > entity.confidence) {
          corrections.push({
            original: entity.raw,
            corrected: bestAlt.value,
            reason: `Higher confidence match found (${Math.round(bestAlt.confidence * 100)}%)`,
            confidence: bestAlt.confidence,
            applied: false, // Let the system decide
          });
        }
      }
    }

    // Check for injury flags on players
    const players = deconstructed.entities.filter(e => e.type === 'player') as PlayerEntity[];
    for (const player of players) {
      if (player.injuryStatus && player.injuryStatus !== 'Healthy') {
        warnings.push(`${player.canonical} is currently ${player.injuryStatus}`);
        dataQualityFlags.push('player_injury');
      }
    }

    // Check date validity
    const dates = deconstructed.entities.filter(e => e.type === 'date') as DateEntity[];
    for (const dateEntity of dates) {
      // Check if date is in the future for historical queries
      if (dateEntity.startDate > new Date() && deconstructed.intent.primary === 'historical_performance') {
        corrections.push({
          original: dateEntity.raw,
          corrected: 'recent games',
          reason: 'Future date specified for historical query',
          confidence: 0.8,
          applied: true,
        });
      }
    }

    // Assess data coverage
    const coverage = await coverageChecker.assessCoverage(
      deconstructed.entities,
      deconstructed.league
    );

    // Add coverage-based warnings
    if (coverage.overall < 0.7) {
      warnings.push(`Data coverage is ${Math.round(coverage.overall * 100)}% - results may be incomplete`);
      dataQualityFlags.push('low_coverage');
    }

    // Add gap descriptions as warnings
    for (const gap of coverage.gaps.filter(g => g.severity !== 'minor')) {
      warnings.push(gap.description);
    }

    // Fetch related data
    const relatedData = await this.fetchRelatedData(deconstructed);

    // Infer missing data if enabled
    const inferredData = this.config.enableInference
      ? await this.inferMissingData(deconstructed, relatedData)
      : [];

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

    // Detect ambiguities
    const ambiguities = queryDeconstructor.detectAmbiguities(deconstructed);
    warnings.push(...ambiguities);

    // Calculate overall validity
    const isValid =
      deconstructed.intent.confidence > 0.3 &&
      coverage.overall > 0.3 &&
      corrections.filter(c => c.applied).length < 3;

    const confidence = this.calculateOverallConfidence(deconstructed, {
      isValid,
      confidence: 0,
      corrections,
      warnings,
      enrichedContext,
    });

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

  /**
   * Fetch related data from database
   */
  private async fetchRelatedData(deconstructed: DeconstructedQuery): Promise<Record<string, unknown>> {
    const relatedData: Record<string, unknown> = {};

    try {
      const prisma = await getSportsPrisma();
      const players = deconstructed.entities.filter(e => e.type === 'player') as PlayerEntity[];
      const dates = deconstructed.entities.filter(e => e.type === 'date') as DateEntity[];

      // Get date range
      let startDate: Date | null = null;
      let endDate: Date | null = null;

      for (const dateEntity of dates) {
        if (!startDate || dateEntity.startDate < startDate) startDate = dateEntity.startDate;
        if (!endDate || dateEntity.endDate > endDate) endDate = dateEntity.endDate;
      }

      // Default to last 30 days
      if (!startDate) {
        endDate = new Date();
        startDate = new Date();
        startDate.setDate(startDate.getDate() - 30);
      }

      // Fetch player stats
      for (const player of players.slice(0, 3)) {
        if (player.externalPlayerId) {
          // Build where clause with league filter to prevent cross-sport matching
          const whereClause: any = {
            playerExternalId: player.externalPlayerId,
            gameDate: {
              gte: startDate,
              lte: endDate,
            },
          };

          // Add league filter if available (critical to avoid cross-sport stat matching)
          const playerLeague = player.league?.toLowerCase();
          if (playerLeague) {
            whereClause.league = { in: [playerLeague, playerLeague.toUpperCase()] };
          }

          const stats = await prisma.playerGameMetric.findMany({
            where: whereClause,
            orderBy: { gameDate: 'desc' },
            take: 10,
          });

          if (stats.length > 0) {
            relatedData[`${player.canonical}_recent_stats`] = stats;
          }
        }
      }

      // Fetch recent games if team context
      const teams = deconstructed.entities.filter(e => e.type === 'team');
      for (const team of teams.slice(0, 2)) {
        const games = await prisma.sportsGame.findMany({
          where: {
            OR: [
              { homeTeam: { contains: team.canonical.split(' ').slice(-1)[0] } },
              { awayTeam: { contains: team.canonical.split(' ').slice(-1)[0] } },
            ],
            gameDate: {
              gte: startDate,
              lte: endDate,
            },
          },
          orderBy: { gameDate: 'desc' },
          take: 10,
        });

        if (games.length > 0) {
          relatedData[`${team.canonical}_recent_games`] = games;
        }
      }

      // Fetch props if prop lookup
      if (deconstructed.intent.primary === 'prop_lookup') {
        for (const player of players.slice(0, 2)) {
          if (player.externalPlayerId) {
            const props = await prisma.playerPropLine.findMany({
              where: {
                playerExternalId: player.externalPlayerId,
              },
              orderBy: { createdAt: 'desc' },
              take: 20,
            });

            if (props.length > 0) {
              relatedData[`${player.canonical}_props`] = props;
            }
          }
        }
      }
    } catch (error) {
      console.error('Error fetching related data:', error);
    }

    return relatedData;
  }

  /**
   * Infer missing data based on available information
   */
  private async inferMissingData(
    deconstructed: DeconstructedQuery,
    relatedData: Record<string, unknown>
  ): Promise<InferredData[]> {
    const inferred: InferredData[] = [];

    try {
      const players = deconstructed.entities.filter(e => e.type === 'player') as PlayerEntity[];

      for (const player of players) {
        const statsKey = `${player.canonical}_recent_stats`;
        const stats = relatedData[statsKey] as any[] | undefined;

        if (stats && stats.length > 0) {
          // Calculate averages for common stats
          const statGroups: Record<string, number[]> = {};

          for (const game of stats) {
            if (!statGroups[game.statKey]) {
              statGroups[game.statKey] = [];
            }
            statGroups[game.statKey].push(game.value);
          }

          for (const [statKey, values] of Object.entries(statGroups)) {
            const avg = values.reduce((a, b) => a + b, 0) / values.length;

            inferred.push({
              field: `${player.canonical}_avg_${statKey}`,
              value: Math.round(avg * 10) / 10,
              basis: `Average of last ${values.length} games`,
              confidence: values.length >= 5 ? 0.85 : 0.7,
              isActual: false,
            });
          }
        }
      }
    } catch (error) {
      console.error('Error inferring data:', error);
    }

    return inferred;
  }

  /**
   * Build enhanced query with corrections applied
   */
  private buildEnhancedQuery(
    deconstructed: DeconstructedQuery,
    validation: ValidationResult
  ): string {
    let enhanced = deconstructed.normalizedQuery;

    // Apply corrections
    for (const correction of validation.corrections) {
      if (correction.applied && correction.confidence > this.config.minConfidenceThreshold) {
        enhanced = enhanced.replace(
          new RegExp(correction.original, 'gi'),
          correction.corrected
        );
      }
    }

    // Add time context if missing
    if (!deconstructed.timeframe) {
      const needsTime =
        deconstructed.intent.primary === 'historical_performance' ||
        deconstructed.intent.primary === 'trend_analysis';

      if (needsTime) {
        enhanced += ' (recent games)';
      }
    }

    // Add league context if detected
    if (deconstructed.league && !enhanced.toLowerCase().includes(deconstructed.league)) {
      enhanced = `[${deconstructed.league.toUpperCase()}] ${enhanced}`;
    }

    return enhanced;
  }

  /**
   * Build context string for LLM consumption
   */
  private buildLLMContext(
    deconstructed: DeconstructedQuery,
    validation: ValidationResult
  ): string {
    const sections: string[] = [];

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

    // Query Analysis
    sections.push('## Query Analysis');
    sections.push(`Intent: ${deconstructed.intent.primary} (${Math.round(deconstructed.intent.confidence * 100)}% confidence)`);
    if (deconstructed.league) {
      sections.push(`League: ${deconstructed.league.toUpperCase()}`);
    }
    if (deconstructed.timeframe) {
      sections.push(`Timeframe: ${deconstructed.timeframe.canonical}`);
    }

    // Resolved Entities
    if (deconstructed.entities.length > 0) {
      sections.push('\n## Resolved Entities');
      for (const entity of deconstructed.entities) {
        const confidence = Math.round(entity.confidence * 100);
        sections.push(`- ${entity.type}: "${entity.raw}" → "${entity.canonical}" (${confidence}%)`);

        if (entity.type === 'player') {
          const player = entity as PlayerEntity;
          if (player.team) sections.push(`  Team: ${player.team}`);
          if (player.injuryStatus) sections.push(`  ⚠️ Injury: ${player.injuryStatus}`);
        }
      }
    }

    // Data Coverage
    sections.push('\n## Data Coverage');
    const coverage = validation.enrichedContext.coverage;
    sections.push(`Overall: ${Math.round(coverage.overall * 100)}%`);
    sections.push(`- Player Stats: ${Math.round(coverage.breakdown.playerStats * 100)}%`);
    sections.push(`- Game Data: ${Math.round(coverage.breakdown.gameData * 100)}%`);
    sections.push(`- Betting Lines: ${Math.round(coverage.breakdown.bettingLines * 100)}%`);
    sections.push(`- Props: ${Math.round(coverage.breakdown.props * 100)}%`);
    sections.push(`- Injuries: ${Math.round(coverage.breakdown.injuries * 100)}%`);

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

    // Corrections Applied
    const appliedCorrections = validation.corrections.filter(c => c.applied);
    if (appliedCorrections.length > 0) {
      sections.push('\n## Corrections Applied');
      for (const correction of appliedCorrections) {
        sections.push(`- "${correction.original}" → "${correction.corrected}" (${correction.reason})`);
      }
    }

    // Data Quality Flags
    if (validation.enrichedContext.dataQualityFlags.length > 0) {
      sections.push('\n## Data Quality Flags');
      sections.push(validation.enrichedContext.dataQualityFlags.join(', '));
    }

    // Inferred Data
    if (validation.enrichedContext.inferredData && validation.enrichedContext.inferredData.length > 0) {
      sections.push('\n## Inferred Data (use with caution)');
      for (const inferred of validation.enrichedContext.inferredData.slice(0, 5)) {
        sections.push(`- ${inferred.field}: ${inferred.value} (${inferred.basis})`);
      }
    }

    // Suggestions
    if (coverage.suggestions.length > 0) {
      sections.push('\n## Suggestions');
      for (const suggestion of coverage.suggestions) {
        sections.push(`💡 ${suggestion}`);
      }
    }

    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 score
   */
  private calculateOverallConfidence(
    deconstructed: DeconstructedQuery,
    validation: ValidationResult
  ): number {
    const weights = {
      intentConfidence: 0.25,
      entityConfidence: 0.30,
      coverageScore: 0.25,
      correctionPenalty: 0.20,
    };

    // Intent confidence
    const intentScore = deconstructed.intent.confidence;

    // Average entity confidence
    const entityScores = deconstructed.entities.map(e => e.confidence);
    const entityScore = entityScores.length > 0
      ? entityScores.reduce((a, b) => a + b, 0) / entityScores.length
      : 1;

    // Coverage score
    const coverageScore = validation.enrichedContext.coverage.overall;

    // Correction penalty (more corrections = lower confidence)
    const correctionCount = validation.corrections.length;
    const correctionScore = Math.max(0, 1 - correctionCount * 0.1);

    return (
      intentScore * weights.intentConfidence +
      entityScore * weights.entityConfidence +
      coverageScore * weights.coverageScore +
      correctionScore * weights.correctionPenalty
    );
  }

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

  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,
    });

    // Cleanup old entries
    if (this.cache.size > 1000) {
      const now = Date.now();
      for (const [k, v] of this.cache.entries()) {
        if (v.expiry < now) {
          this.cache.delete(k);
        }
      }
    }
  }

  /**
   * Get a summary suitable for logging
   */
  getSummary(response: DataSheriffResponse): string {
    return [
      `Query: "${response.deconstructedQuery.originalQuery}"`,
      `Intent: ${response.deconstructedQuery.intent.primary}`,
      `Entities: ${response.metadata.entitiesResolved}`,
      `Coverage: ${Math.round(response.metadata.coverageScore * 100)}%`,
      `Confidence: ${Math.round(response.metadata.confidenceScore * 100)}%`,
      `Time: ${response.processingTimeMs}ms`,
    ].join(' | ');
  }
}

// Export singleton instance
export const dataSheriffAgent = new DataSheriffAgent();

// Export factory for custom configs
export function createDataSheriffAgent(config?: Partial<DataSheriffConfig>): DataSheriffAgent {
  return new DataSheriffAgent(config);
}
