/**
 * Coverage Checker Service
 * Assesses data completeness for queries
 */

import {
  CoverageReport,
  DataGap,
  ResolvedEntity,
  PlayerEntity,
  TeamEntity,
  DateEntity,
} from './types';

// Prisma clients - lazy loaded
let mainPrisma: any = null;
let sportsPrisma: any = null;

async function getMainPrisma() {
  if (!mainPrisma) {
    try {
      const { PrismaClient } = await import('@prisma/client');
      mainPrisma = new PrismaClient();
    } catch {
      return null;
    }
  }
  return mainPrisma;
}

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

export class CoverageChecker {
  /**
   * Assess overall data coverage for a set of resolved entities
   */
  async assessCoverage(
    entities: ResolvedEntity[],
    league?: string
  ): Promise<CoverageReport> {
    const players = entities.filter(e => e.type === 'player') as PlayerEntity[];
    const teams = entities.filter(e => e.type === 'team') as TeamEntity[];
    const dates = entities.filter(e => e.type === 'date') as DateEntity[];

    const gaps: DataGap[] = [];
    const suggestions: string[] = [];

    // 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 no date specified
    if (!startDate) {
      endDate = new Date();
      startDate = new Date();
      startDate.setDate(startDate.getDate() - 30);
    }

    // Check coverage for each category
    const playerStatsCoverage = await this.checkPlayerStatsCoverage(
      players,
      startDate,
      endDate,
      league,
      gaps
    );

    const gameDataCoverage = await this.checkGameDataCoverage(
      teams,
      startDate,
      endDate,
      league,
      gaps
    );

    const bettingLinesCoverage = await this.checkBettingLinesCoverage(
      teams,
      startDate,
      endDate,
      league,
      gaps
    );

    const propsCoverage = await this.checkPropsCoverage(
      players,
      startDate,
      endDate,
      league,
      gaps
    );

    const injuriesCoverage = await this.checkInjuriesCoverage(
      players,
      league,
      gaps
    );

    // Calculate overall score
    const weights = {
      playerStats: 0.3,
      gameData: 0.25,
      bettingLines: 0.2,
      props: 0.15,
      injuries: 0.1,
    };

    const overall =
      playerStatsCoverage * weights.playerStats +
      gameDataCoverage * weights.gameData +
      bettingLinesCoverage * weights.bettingLines +
      propsCoverage * weights.props +
      injuriesCoverage * weights.injuries;

    // Generate suggestions based on gaps
    if (playerStatsCoverage < 0.8) {
      suggestions.push('Player stats data may be incomplete. Consider recent games only.');
    }
    if (bettingLinesCoverage < 0.7) {
      suggestions.push('Betting lines data is limited. Live odds may not be available.');
    }
    if (propsCoverage < 0.5) {
      suggestions.push('Player props data is sparse. Historical averages will be used.');
    }

    return {
      overall,
      breakdown: {
        playerStats: playerStatsCoverage,
        gameData: gameDataCoverage,
        bettingLines: bettingLinesCoverage,
        props: propsCoverage,
        injuries: injuriesCoverage,
      },
      gaps,
      suggestions,
    };
  }

  /**
   * Check player stats coverage
   */
  private async checkPlayerStatsCoverage(
    players: PlayerEntity[],
    startDate: Date,
    endDate: Date,
    league?: string,
    gaps?: DataGap[]
  ): Promise<number> {
    if (players.length === 0) {
      return 1.0; // No players requested = 100% coverage
    }

    try {
      const prisma = await getSportsPrisma();

      let totalExpected = 0;
      let totalFound = 0;

      for (const player of players) {
        // Count games in date range
        const gamesInRange = await prisma.sportsGame.count({
          where: {
            gameDate: {
              gte: startDate,
              lte: endDate,
            },
            league: league?.toUpperCase() || player.league.toUpperCase(),
          },
        });

        // Estimate expected stats (assume player plays 80% of games)
        const expectedStats = Math.ceil(gamesInRange * 0.8);
        totalExpected += expectedStats;

        // Count actual stats
        const actualStats = await prisma.playerGameMetric.count({
          where: {
            playerExternalId: player.externalPlayerId || player.playerId?.toString(),
            gameDate: {
              gte: startDate,
              lte: endDate,
            },
          },
        });

        totalFound += actualStats;

        // Flag gap if significant
        if (actualStats < expectedStats * 0.5 && gaps) {
          gaps.push({
            type: 'player_stats',
            description: `Missing stats for ${player.canonical} (${actualStats}/${expectedStats} games)`,
            severity: actualStats === 0 ? 'critical' : 'moderate',
            workaround: 'Using season averages where available',
          });
        }
      }

      return totalExpected > 0 ? Math.min(totalFound / totalExpected, 1.0) : 1.0;
    } catch (error) {
      console.error('Error checking player stats coverage:', error);
      return 0.5; // Assume partial coverage on error
    }
  }

  /**
   * Check game data coverage
   */
  private async checkGameDataCoverage(
    teams: TeamEntity[],
    startDate: Date,
    endDate: Date,
    league?: string,
    gaps?: DataGap[]
  ): Promise<number> {
    try {
      const prisma = await getSportsPrisma();

      // Count games in date range
      const whereClause: any = {
        gameDate: {
          gte: startDate,
          lte: endDate,
        },
      };

      if (league) {
        whereClause.league = league.toUpperCase();
      }

      if (teams.length > 0) {
        const teamNames = teams.map(t => t.canonical);
        whereClause.OR = [
          { homeTeam: { in: teamNames } },
          { awayTeam: { in: teamNames } },
        ];
      }

      const gamesWithScores = await prisma.sportsGame.count({
        where: {
          ...whereClause,
          homeScore: { not: null },
          awayScore: { not: null },
        },
      });

      const totalGames = await prisma.sportsGame.count({
        where: whereClause,
      });

      // Check for incomplete games
      const incompleteGames = totalGames - gamesWithScores;
      if (incompleteGames > 0 && gaps) {
        gaps.push({
          type: 'game_data',
          description: `${incompleteGames} games missing final scores`,
          severity: incompleteGames > 5 ? 'moderate' : 'minor',
          workaround: 'Games may still be in progress or data pending',
        });
      }

      return totalGames > 0 ? gamesWithScores / totalGames : 1.0;
    } catch (error) {
      console.error('Error checking game data coverage:', error);
      return 0.5;
    }
  }

  /**
   * Check betting lines coverage
   */
  private async checkBettingLinesCoverage(
    teams: TeamEntity[],
    startDate: Date,
    endDate: Date,
    league?: string,
    gaps?: DataGap[]
  ): Promise<number> {
    try {
      const prisma = await getSportsPrisma();

      const whereClause: any = {
        gameDate: {
          gte: startDate,
          lte: endDate,
        },
      };

      if (league) {
        whereClause.league = league.toUpperCase();
      }

      // Count games with betting lines
      const gamesWithOdds = await prisma.sportsGame.count({
        where: {
          ...whereClause,
          OR: [
            { moneylineHome: { not: null } },
            { spreadHome: { not: null } },
            { total: { not: null } },
          ],
        },
      });

      const totalGames = await prisma.sportsGame.count({
        where: whereClause,
      });

      const coverage = totalGames > 0 ? gamesWithOdds / totalGames : 0;

      if (coverage < 0.7 && gaps) {
        gaps.push({
          type: 'betting_lines',
          description: `Only ${Math.round(coverage * 100)}% of games have betting lines`,
          severity: coverage < 0.5 ? 'moderate' : 'minor',
          workaround: 'Historical odds may be unavailable for some games',
        });
      }

      return coverage;
    } catch (error) {
      console.error('Error checking betting lines coverage:', error);
      return 0.3;
    }
  }

  /**
   * Check player props coverage
   */
  private async checkPropsCoverage(
    players: PlayerEntity[],
    startDate: Date,
    endDate: Date,
    league?: string,
    gaps?: DataGap[]
  ): Promise<number> {
    if (players.length === 0) {
      return 1.0;
    }

    try {
      const prisma = await getSportsPrisma();

      let totalPlayers = players.length;
      let playersWithProps = 0;

      for (const player of players) {
        const propsCount = await prisma.playerPropLine.count({
          where: {
            playerExternalId: player.externalPlayerId || player.playerId?.toString() || '',
            league: league?.toUpperCase() || player.league.toUpperCase(),
          },
          take: 1,
        });

        if (propsCount > 0) {
          playersWithProps++;
        }
      }

      const coverage = playersWithProps / totalPlayers;

      if (coverage < 0.5 && gaps) {
        const missingPlayers = players.filter(p => !p.externalPlayerId).map(p => p.canonical);
        gaps.push({
          type: 'player_props',
          description: `Props data unavailable for ${totalPlayers - playersWithProps}/${totalPlayers} players`,
          severity: 'moderate',
          workaround: 'Using historical stat averages for projections',
        });
      }

      return coverage;
    } catch (error) {
      console.error('Error checking props coverage:', error);
      return 0.2;
    }
  }

  /**
   * Check injuries coverage
   */
  private async checkInjuriesCoverage(
    players: PlayerEntity[],
    league?: string,
    gaps?: DataGap[]
  ): Promise<number> {
    try {
      const prisma = await getSportsPrisma();

      // Check if we have recent injury data (within 24 hours)
      const recentInjuries = await prisma.playerInjury.count({
        where: {
          updatedAt: {
            gte: new Date(Date.now() - 24 * 60 * 60 * 1000),
          },
          ...(league ? { league: league.toUpperCase() } : {}),
        },
      });

      // If we have any recent injury updates, consider it covered
      const coverage = recentInjuries > 0 ? 0.9 : 0.3;

      if (coverage < 0.5 && gaps) {
        gaps.push({
          type: 'injuries',
          description: 'Injury data may be stale (>24 hours old)',
          severity: 'minor',
          workaround: 'Check official sources for latest injury updates',
        });
      }

      return coverage;
    } catch (error) {
      console.error('Error checking injuries coverage:', error);
      return 0.5;
    }
  }

  /**
   * Quick check if specific data exists
   */
  async quickCheck(
    entityType: 'player' | 'team' | 'game',
    identifier: string,
    league?: string
  ): Promise<{ exists: boolean; details?: any }> {
    try {
      const prisma = await getSportsPrisma();

      switch (entityType) {
        case 'player': {
          const player = await prisma.player.findFirst({
            where: {
              OR: [
                { name: { contains: identifier } },
                { externalPlayerId: identifier },
              ],
              ...(league ? { league: league.toUpperCase() } : {}),
            },
          });
          return { exists: !!player, details: player };
        }

        case 'team': {
          const game = await prisma.sportsGame.findFirst({
            where: {
              OR: [
                { homeTeam: { contains: identifier } },
                { awayTeam: { contains: identifier } },
              ],
              ...(league ? { league: league.toUpperCase() } : {}),
            },
            orderBy: { gameDate: 'desc' },
          });
          return { exists: !!game, details: game };
        }

        case 'game': {
          const game = await prisma.sportsGame.findFirst({
            where: {
              externalGameId: identifier,
            },
          });
          return { exists: !!game, details: game };
        }

        default:
          return { exists: false };
      }
    } catch (error) {
      console.error('Error in quick check:', error);
      return { exists: false };
    }
  }

  /**
   * Get data freshness info
   */
  async getDataFreshness(league?: string): Promise<{
    lastGameUpdate: Date | null;
    lastOddsUpdate: Date | null;
    lastPlayerUpdate: Date | null;
    lastInjuryUpdate: Date | null;
  }> {
    try {
      const prisma = await getSportsPrisma();

      const whereClause = league ? { league: league.toUpperCase() } : {};

      const [lastGame, lastOdds, lastPlayer, lastInjury] = await Promise.all([
        prisma.sportsGame.findFirst({
          where: whereClause,
          orderBy: { updatedAt: 'desc' },
          select: { updatedAt: true },
        }),
        prisma.sportsGame.findFirst({
          where: {
            ...whereClause,
            oddsUpdatedAt: { not: null },
          },
          orderBy: { oddsUpdatedAt: 'desc' },
          select: { oddsUpdatedAt: true },
        }),
        prisma.player.findFirst({
          where: whereClause,
          orderBy: { updatedAt: 'desc' },
          select: { updatedAt: true },
        }),
        prisma.playerInjury.findFirst({
          where: whereClause,
          orderBy: { updatedAt: 'desc' },
          select: { updatedAt: true },
        }),
      ]);

      return {
        lastGameUpdate: lastGame?.updatedAt || null,
        lastOddsUpdate: lastOdds?.oddsUpdatedAt || null,
        lastPlayerUpdate: lastPlayer?.updatedAt || null,
        lastInjuryUpdate: lastInjury?.updatedAt || null,
      };
    } catch (error) {
      console.error('Error getting data freshness:', error);
      return {
        lastGameUpdate: null,
        lastOddsUpdate: null,
        lastPlayerUpdate: null,
        lastInjuryUpdate: null,
      };
    }
  }

  /**
   * Get coverage summary for a league
   */
  async getLeagueCoverageSummary(league: string): Promise<{
    totalGames: number;
    gamesWithScores: number;
    gamesWithOdds: number;
    totalPlayers: number;
    playersWithStats: number;
    dateRange: { earliest: Date | null; latest: Date | null };
  }> {
    try {
      const prisma = await getSportsPrisma();

      const [
        totalGames,
        gamesWithScores,
        gamesWithOdds,
        totalPlayers,
        playersWithStats,
        dateRange,
      ] = await Promise.all([
        prisma.sportsGame.count({ where: { league: league.toUpperCase() } }),
        prisma.sportsGame.count({
          where: { league: league.toUpperCase(), homeScore: { not: null } },
        }),
        prisma.sportsGame.count({
          where: { league: league.toUpperCase(), moneylineHome: { not: null } },
        }),
        prisma.player.count({ where: { league: league.toUpperCase() } }),
        prisma.playerGameMetric.groupBy({
          by: ['playerExternalId'],
          where: { league: league.toUpperCase() },
        }).then(r => r.length),
        prisma.sportsGame.aggregate({
          where: { league: league.toUpperCase() },
          _min: { gameDate: true },
          _max: { gameDate: true },
        }),
      ]);

      return {
        totalGames,
        gamesWithScores,
        gamesWithOdds,
        totalPlayers,
        playersWithStats,
        dateRange: {
          earliest: dateRange._min.gameDate,
          latest: dateRange._max.gameDate,
        },
      };
    } catch (error) {
      console.error('Error getting league coverage summary:', error);
      return {
        totalGames: 0,
        gamesWithScores: 0,
        gamesWithOdds: 0,
        totalPlayers: 0,
        playersWithStats: 0,
        dateRange: { earliest: null, latest: null },
      };
    }
  }
}

export const coverageChecker = new CoverageChecker();
