#!/usr/bin/env npx tsx
/**
 * Data Integrity & Accuracy Stress Test
 * 40 Targeted Queries to verify database consistency and accuracy
 */
require('dotenv').config({ path: '.env.local' });
require('dotenv').config({ path: '.env' });

import { getSportsDataForQuery } from '../lib/chat';
import { getSportsDb, isSportsDbEnabled } from '../lib/sportsDb';

interface TestQuery {
  id: number;
  category: string;
  subcategory: string;
  question: string;
  expectedAnswer?: string;
  groundTruth?: any;
  verifiable: boolean;
}

interface TestResult {
  id: number;
  category: string;
  subcategory: string;
  question: string;
  expectedAnswer?: string;
  actualResponse: string;
  responseLength: number;
  executionTimeMs: number;
  accuracy: 'PERFECT' | 'ACCEPTABLE' | 'POOR' | 'FAIL' | 'NO_DATA' | 'GAP_ACKNOWLEDGED';
  issues: string[];
  dataProvenance: {
    hasSource: boolean;
    hasTimestamp: boolean;
    hasCoverage: boolean;
    acknowledgesGaps: boolean;
  };
  score: number;
}

const TEST_QUERIES: TestQuery[] = [
  // Category 1: Verifiable Historical Facts
  // 1.1 Exact Game Results
  { id: 1, category: 'Historical Facts', subcategory: 'Game Results', question: 'What was the exact final score of Super Bowl LVIII (Chiefs vs 49ers)?', expectedAnswer: '25-22 OT, Chiefs won', verifiable: true },
  { id: 2, category: 'Historical Facts', subcategory: 'Game Results', question: 'Show me the box score for Lakers vs Celtics on Christmas Day 2023.', verifiable: true },
  { id: 3, category: 'Historical Facts', subcategory: 'Game Results', question: "What was Nikola Jokic's stat line in Game 5 of the 2023 NBA Finals?", expectedAnswer: '28 pts, 16 reb, 4 ast', verifiable: true },
  { id: 4, category: 'Historical Facts', subcategory: 'Game Results', question: 'What was the spread and total for Bengals vs Bills in the 2023 AFC Divisional Round?', expectedAnswer: 'BUF -5.5, Total 49.5', verifiable: true },
  { id: 5, category: 'Historical Facts', subcategory: 'Game Results', question: "Show me Connor McDavid's points in Game 4 of the 2023 Stanley Cup Finals.", expectedAnswer: '3 assists, 0 goals', verifiable: true },

  // 1.2 Player Career Milestones
  { id: 6, category: 'Historical Facts', subcategory: 'Milestones', question: 'When did LeBron James score his 40,000th point and against which team?', expectedAnswer: 'Mar 2, 2024 vs Nuggets', verifiable: true },
  { id: 7, category: 'Historical Facts', subcategory: 'Milestones', question: "What was Patrick Mahomes' exact passer rating in his first Super Bowl win?", expectedAnswer: '78.1 in Super Bowl LIV', verifiable: true },
  { id: 8, category: 'Historical Facts', subcategory: 'Milestones', question: "Show me Shohei Ohtani's home run total for the 2023 MLB season.", expectedAnswer: '44 HR', verifiable: true },
  { id: 9, category: 'Historical Facts', subcategory: 'Milestones', question: "What was Lionel Messi's goal count in the 2022 World Cup?", expectedAnswer: '7 goals', verifiable: true },
  { id: 10, category: 'Historical Facts', subcategory: 'Milestones', question: 'How many triple-doubles does Luka Doncic have in his career through 2024?', expectedAnswer: '72 through Dec 2024', verifiable: true },

  // Category 2: Database Consistency Checks
  // 2.1 Cross-Reference Validation
  { id: 11, category: 'Consistency', subcategory: 'Cross-Reference', question: "Show me LeBron's points and rebounds in the same game where Anthony Davis had 40+ points.", expectedAnswer: 'Dec 13, 2023 vs Mavs', verifiable: true },
  { id: 12, category: 'Consistency', subcategory: 'Cross-Reference', question: 'Find all games where both Patrick Mahomes and Josh Allen threw 3+ TDs.', verifiable: true },
  { id: 13, category: 'Consistency', subcategory: 'Cross-Reference', question: 'Show me games where a team won but failed to cover the spread.', verifiable: true },
  { id: 14, category: 'Consistency', subcategory: 'Cross-Reference', question: 'Find players who had 30+ points and 10+ assists in the same game in 2024.', verifiable: true },
  { id: 15, category: 'Consistency', subcategory: 'Cross-Reference', question: 'Show me NHL games where the over hit but the favorite lost.', verifiable: true },

  // 2.2 Temporal Consistency
  { id: 16, category: 'Consistency', subcategory: 'Temporal', question: "Show me Stephen Curry's stats in the game immediately after his 62-point performance.", verifiable: true },
  { id: 17, category: 'Consistency', subcategory: 'Temporal', question: "What was the Miami Heat's record in games following a loss in the 2023 playoffs?", verifiable: true },
  { id: 18, category: 'Consistency', subcategory: 'Temporal', question: 'Find instances where a team was favored by 10+ points but lost outright.', verifiable: true },
  { id: 19, category: 'Consistency', subcategory: 'Temporal', question: 'Show me the longest winning streak for any NBA team in the 2023-24 season.', expectedAnswer: 'Celtics: 11 games', verifiable: true },
  { id: 20, category: 'Consistency', subcategory: 'Temporal', question: 'What was the biggest comeback in the 2023 NFL season?', expectedAnswer: 'Colts vs Vikings: 33-point deficit', verifiable: true },

  // Category 3: Known Data Gap Testing
  // 3.1 Intentional Missing Data
  { id: 21, category: 'Data Gaps', subcategory: 'Missing Data', question: 'Show me player prop bets for Warriors vs Kings on October 18, 2023.', verifiable: false },
  { id: 22, category: 'Data Gaps', subcategory: 'Missing Data', question: 'What were the advanced analytics (PIE, PER) for the 1998 NBA Finals?', verifiable: false },
  { id: 23, category: 'Data Gaps', subcategory: 'Missing Data', question: 'Show me public betting percentages for NCAA tournament games before 2010.', verifiable: false },
  { id: 24, category: 'Data Gaps', subcategory: 'Missing Data', question: 'Find the expected goals (xG) data for NHL games in the 2015 season.', verifiable: false },
  { id: 25, category: 'Data Gaps', subcategory: 'Missing Data', question: "What was the velocity on Shohei Ohtani's pitches in his 2018 MLB starts?", verifiable: false },

  // 3.2 Partial Data Scenarios
  { id: 26, category: 'Data Gaps', subcategory: 'Partial Data', question: 'Show me all player stats for the canceled March 11, 2020 NBA games.', verifiable: false },
  { id: 27, category: 'Data Gaps', subcategory: 'Partial Data', question: 'What were the betting lines for the postponed Yankees vs Red Sox game on April 10, 2023?', verifiable: false },
  { id: 28, category: 'Data Gaps', subcategory: 'Partial Data', question: 'Find the prop bets for players who were injured in the first quarter.', verifiable: false },
  { id: 29, category: 'Data Gaps', subcategory: 'Partial Data', question: 'Show me stats for players traded mid-game (like NBA deadline deals).', verifiable: false },
  { id: 30, category: 'Data Gaps', subcategory: 'Partial Data', question: 'What were the advanced metrics for games with arena power outages?', verifiable: false },

  // Category 4: Edge Case Data Retrieval
  // 4.1 Ambiguous But Verifiable
  { id: 31, category: 'Edge Cases', subcategory: 'Ambiguous', question: "Show me Michael Jordan's stats in games where he played against Kobe Bryant.", verifiable: true },
  { id: 32, category: 'Edge Cases', subcategory: 'Ambiguous', question: 'Find games where Tom Brady and Peyton Manning both threw for 300+ yards.', verifiable: true },
  { id: 33, category: 'Edge Cases', subcategory: 'Ambiguous', question: "What was LeBron's scoring average in games played on Tuesdays in March?", verifiable: true },
  { id: 34, category: 'Edge Cases', subcategory: 'Ambiguous', question: 'Show me all overtime games in the 2023 NFL season where both teams scored.', verifiable: true },
  { id: 35, category: 'Edge Cases', subcategory: 'Ambiguous', question: 'Find NHL games where a goalie had 50+ saves but still lost.', verifiable: true },

  // 4.2 Complex Joins Required
  { id: 36, category: 'Edge Cases', subcategory: 'Complex Joins', question: 'Show me players who scored 50+ points in a game their team lost by 20+ points.', verifiable: true },
  { id: 37, category: 'Edge Cases', subcategory: 'Complex Joins', question: 'Find games where the underdog won outright and the under hit.', verifiable: true },
  { id: 38, category: 'Edge Cases', subcategory: 'Complex Joins', question: 'Show me QBs who threw 4+ TDs in a game where their defense allowed 30+ points.', verifiable: true },
  { id: 39, category: 'Edge Cases', subcategory: 'Complex Joins', question: 'Find NBA players who had a triple-double while shooting under 40% from the field.', verifiable: true },
  { id: 40, category: 'Edge Cases', subcategory: 'Complex Joins', question: 'Show me MLB games where a pitcher threw a complete game shutout with 10+ strikeouts and 0 walks.', verifiable: true },
];

function analyzeResponse(query: TestQuery, response: string, executionTimeMs: number): TestResult {
  const issues: string[] = [];
  const responseLower = response.toLowerCase();

  // Data provenance checks
  const dataProvenance = {
    hasSource: /source|sportsdb|database|official|verified/i.test(response),
    hasTimestamp: /\d{4}-\d{2}-\d{2}|january|february|march|april|may|june|july|august|september|october|november|december \d+/i.test(response),
    hasCoverage: /coverage|complete|partial|available/i.test(response),
    acknowledgesGaps: /not available|no data|missing|unavailable|don't have|cannot find|gap|pre-\d{4}|before \d{4}|off-season/i.test(response),
  };

  // Check for fabrication indicators
  const fabricationIndicators = [
    /approximately|roughly|around|estimated|likely|probably|might be/i,
    /i think|i believe|based on my knowledge/i,
  ];

  const hasFabrication = fabricationIndicators.some(re => re.test(response));
  if (hasFabrication && query.verifiable) {
    issues.push('Response contains uncertainty language for verifiable fact');
  }

  // Check for data mixing (wrong sport/player/team)
  if (query.question.toLowerCase().includes('nba') && /nfl|mlb|nhl/i.test(response) && !/cross-sport/i.test(response)) {
    issues.push('Potential sport data mixing detected');
  }

  // Check response length
  if (response.length < 50) {
    issues.push('Response too short - may be incomplete');
  }

  // Check for "no data" vs actual data
  const hasNoData = /no .*(data|games?|results?|information|stats?) (found|available)|unavailable|not found|don't have/i.test(response);
  const hasActualData = /\d+\s*(pts?|points?|reb|rebounds?|ast|assists?|goals?|tds?|touchdowns?|yards?|hr|home runs?)/i.test(response);

  // Accuracy scoring
  let accuracy: TestResult['accuracy'] = 'ACCEPTABLE';
  let score = 7;

  if (hasNoData && !query.verifiable) {
    // Expected gap - should acknowledge it properly
    if (dataProvenance.acknowledgesGaps) {
      accuracy = 'GAP_ACKNOWLEDGED';
      score = 9;
    } else {
      accuracy = 'NO_DATA';
      score = 5;
      issues.push('Data gap not properly explained');
    }
  } else if (hasNoData && query.verifiable) {
    accuracy = 'FAIL';
    score = 2;
    issues.push('No data for verifiable query - database gap or query failure');
  } else if (hasActualData) {
    // Check if expected answer matches (if provided)
    if (query.expectedAnswer) {
      const expectedParts = query.expectedAnswer.toLowerCase().split(/[,\s]+/);
      const matchCount = expectedParts.filter(part =>
        part.length > 2 && responseLower.includes(part)
      ).length;
      const matchRatio = matchCount / expectedParts.length;

      if (matchRatio > 0.7) {
        accuracy = 'PERFECT';
        score = 10;
      } else if (matchRatio > 0.4) {
        accuracy = 'ACCEPTABLE';
        score = 8;
      } else {
        accuracy = 'POOR';
        score = 5;
        issues.push(`Expected answer not fully matched (${Math.round(matchRatio * 100)}% match)`);
      }
    } else {
      // No expected answer - judge on response quality
      if (dataProvenance.hasSource && response.length > 200) {
        accuracy = 'ACCEPTABLE';
        score = 8;
      }
    }
  }

  // Deduct for issues
  score = Math.max(0, score - issues.length * 0.5);

  return {
    id: query.id,
    category: query.category,
    subcategory: query.subcategory,
    question: query.question,
    expectedAnswer: query.expectedAnswer,
    actualResponse: response.substring(0, 500) + (response.length > 500 ? '...' : ''),
    responseLength: response.length,
    executionTimeMs,
    accuracy,
    issues,
    dataProvenance,
    score: Math.round(score * 10) / 10,
  };
}

async function runDatabaseStats() {
  if (!isSportsDbEnabled()) {
    console.log('SportsDB not enabled - skipping database stats');
    return null;
  }

  const db = getSportsDb();
  const stats: Record<string, any> = {};

  // Get counts and date ranges for each table
  for (const league of ['nba', 'nfl', 'nhl', 'mlb']) {
    const gameCount = await db.sportsGame.count({ where: { league } });
    const latestGame = await db.sportsGame.findFirst({
      where: { league },
      orderBy: { gameDate: 'desc' },
      select: { gameDate: true }
    });
    const earliestGame = await db.sportsGame.findFirst({
      where: { league },
      orderBy: { gameDate: 'asc' },
      select: { gameDate: true }
    });

    stats[league] = {
      gameCount,
      dateRange: {
        earliest: earliestGame?.gameDate?.toISOString().split('T')[0] || 'N/A',
        latest: latestGame?.gameDate?.toISOString().split('T')[0] || 'N/A',
      }
    };
  }

  // Player metrics
  const playerMetricsCount = await db.playerGameMetric.count();

  return {
    ...stats,
    playerMetrics: playerMetricsCount,
  };
}

async function runAudit() {
  console.log('='.repeat(80));
  console.log('DATA INTEGRITY & ACCURACY STRESS TEST');
  console.log('40 Targeted Queries');
  console.log('='.repeat(80));
  console.log(`Started: ${new Date().toISOString()}`);
  console.log();

  // Database statistics
  console.log('DATABASE STATISTICS:');
  console.log('-'.repeat(40));
  const dbStats = await runDatabaseStats();
  if (dbStats) {
    for (const [key, value] of Object.entries(dbStats)) {
      if (typeof value === 'object' && value.gameCount !== undefined) {
        console.log(`${key.toUpperCase()}: ${value.gameCount} games (${value.dateRange.earliest} to ${value.dateRange.latest})`);
      } else if (key === 'playerMetrics') {
        console.log(`Player Metrics: ${value} records`);
      }
    }
  }
  console.log();

  const results: TestResult[] = [];
  const delayMs = 2500; // Avoid rate limits

  // Group by category for organized output
  const categories = [...new Set(TEST_QUERIES.map(q => q.category))];

  for (const category of categories) {
    console.log('\n' + '='.repeat(60));
    console.log(`CATEGORY: ${category.toUpperCase()}`);
    console.log('='.repeat(60));

    const categoryQueries = TEST_QUERIES.filter(q => q.category === category);

    for (const query of categoryQueries) {
      console.log(`\n[${query.id}/40] ${query.subcategory}: ${query.question.substring(0, 60)}...`);

      const startTime = Date.now();
      try {
        const response = await getSportsDataForQuery(query.question);
        const executionTime = Date.now() - startTime;
        const context = response?.context || 'No response';

        const result = analyzeResponse(query, context, executionTime);
        results.push(result);

        const statusIcon = {
          'PERFECT': '\u2705',
          'ACCEPTABLE': '\u2705',
          'POOR': '\u26A0\uFE0F',
          'FAIL': '\u274C',
          'NO_DATA': '\uD83D\uDCED',
          'GAP_ACKNOWLEDGED': '\u2139\uFE0F'
        }[result.accuracy];

        console.log(`    ${statusIcon} ${result.accuracy} (score: ${result.score}/10, ${executionTime}ms, ${result.responseLength} chars)`);
        if (result.issues.length > 0) {
          result.issues.forEach(issue => console.log(`       - ${issue}`));
        }
        if (result.actualResponse.length < 200) {
          console.log(`    Response: ${result.actualResponse.substring(0, 150)}...`);
        }

      } catch (error) {
        const executionTime = Date.now() - startTime;
        results.push({
          id: query.id,
          category: query.category,
          subcategory: query.subcategory,
          question: query.question,
          expectedAnswer: query.expectedAnswer,
          actualResponse: `ERROR: ${error}`,
          responseLength: 0,
          executionTimeMs: executionTime,
          accuracy: 'FAIL',
          issues: [`Exception: ${error}`],
          dataProvenance: { hasSource: false, hasTimestamp: false, hasCoverage: false, acknowledgesGaps: false },
          score: 0,
        });
        console.log(`    \u274C ERROR: ${error}`);
      }

      await new Promise(resolve => setTimeout(resolve, delayMs));
    }
  }

  // Generate summary report
  console.log('\n' + '='.repeat(80));
  console.log('AUDIT SUMMARY REPORT');
  console.log('='.repeat(80));

  const byAccuracy = {
    PERFECT: results.filter(r => r.accuracy === 'PERFECT'),
    ACCEPTABLE: results.filter(r => r.accuracy === 'ACCEPTABLE'),
    POOR: results.filter(r => r.accuracy === 'POOR'),
    FAIL: results.filter(r => r.accuracy === 'FAIL'),
    NO_DATA: results.filter(r => r.accuracy === 'NO_DATA'),
    GAP_ACKNOWLEDGED: results.filter(r => r.accuracy === 'GAP_ACKNOWLEDGED'),
  };

  console.log('\nOVERALL RESULTS:');
  console.log(`  \u2705 PERFECT:          ${byAccuracy.PERFECT.length}/40 (${(byAccuracy.PERFECT.length/40*100).toFixed(1)}%)`);
  console.log(`  \u2705 ACCEPTABLE:       ${byAccuracy.ACCEPTABLE.length}/40 (${(byAccuracy.ACCEPTABLE.length/40*100).toFixed(1)}%)`);
  console.log(`  \u26A0\uFE0F  POOR:            ${byAccuracy.POOR.length}/40 (${(byAccuracy.POOR.length/40*100).toFixed(1)}%)`);
  console.log(`  \u274C FAIL:            ${byAccuracy.FAIL.length}/40 (${(byAccuracy.FAIL.length/40*100).toFixed(1)}%)`);
  console.log(`  \uD83D\uDCED NO_DATA:         ${byAccuracy.NO_DATA.length}/40 (${(byAccuracy.NO_DATA.length/40*100).toFixed(1)}%)`);
  console.log(`  \u2139\uFE0F  GAP_ACKNOWLEDGED: ${byAccuracy.GAP_ACKNOWLEDGED.length}/40 (${(byAccuracy.GAP_ACKNOWLEDGED.length/40*100).toFixed(1)}%)`);

  // Calculate overall score
  const totalScore = results.reduce((sum, r) => sum + r.score, 0);
  const avgScore = totalScore / results.length;
  const accuracyScore = ((byAccuracy.PERFECT.length * 10 + byAccuracy.ACCEPTABLE.length * 8 +
                          byAccuracy.GAP_ACKNOWLEDGED.length * 9 + byAccuracy.POOR.length * 5) /
                         (40 * 10)) * 100;

  console.log(`\nOVERALL SCORE: ${avgScore.toFixed(1)}/10`);
  console.log(`ACCURACY SCORE: ${accuracyScore.toFixed(1)}%`);

  // By category breakdown
  console.log('\nRESULTS BY CATEGORY:');
  for (const category of categories) {
    const catResults = results.filter(r => r.category === category);
    const catScore = catResults.reduce((sum, r) => sum + r.score, 0) / catResults.length;
    const passed = catResults.filter(r => ['PERFECT', 'ACCEPTABLE', 'GAP_ACKNOWLEDGED'].includes(r.accuracy)).length;
    console.log(`  ${category}: ${passed}/${catResults.length} passing (avg score: ${catScore.toFixed(1)}/10)`);
  }

  // Data provenance analysis
  console.log('\nDATA PROVENANCE ANALYSIS:');
  const provenanceStats = {
    hasSource: results.filter(r => r.dataProvenance.hasSource).length,
    hasTimestamp: results.filter(r => r.dataProvenance.hasTimestamp).length,
    hasCoverage: results.filter(r => r.dataProvenance.hasCoverage).length,
    acknowledgesGaps: results.filter(r => r.dataProvenance.acknowledgesGaps).length,
  };
  console.log(`  Responses with source attribution: ${provenanceStats.hasSource}/40 (${(provenanceStats.hasSource/40*100).toFixed(1)}%)`);
  console.log(`  Responses with timestamps: ${provenanceStats.hasTimestamp}/40 (${(provenanceStats.hasTimestamp/40*100).toFixed(1)}%)`);
  console.log(`  Responses noting coverage: ${provenanceStats.hasCoverage}/40 (${(provenanceStats.hasCoverage/40*100).toFixed(1)}%)`);
  console.log(`  Responses acknowledging gaps: ${provenanceStats.acknowledgesGaps}/40 (${(provenanceStats.acknowledgesGaps/40*100).toFixed(1)}%)`);

  // Issues summary
  console.log('\nCOMMON ISSUES FOUND:');
  const allIssues: Record<string, number> = {};
  results.forEach(r => {
    r.issues.forEach(issue => {
      const key = issue.split('(')[0].trim();
      allIssues[key] = (allIssues[key] || 0) + 1;
    });
  });
  Object.entries(allIssues)
    .sort((a, b) => b[1] - a[1])
    .slice(0, 10)
    .forEach(([issue, count]) => {
      console.log(`  - ${issue}: ${count} occurrences`);
    });

  // Failed queries detail
  const failedQueries = [...byAccuracy.FAIL, ...byAccuracy.POOR];
  if (failedQueries.length > 0) {
    console.log('\nFAILED/POOR QUERIES:');
    failedQueries.forEach(r => {
      console.log(`  [${r.id}] ${r.question.substring(0, 50)}...`);
      console.log(`       Accuracy: ${r.accuracy}, Score: ${r.score}/10`);
      r.issues.forEach(issue => console.log(`       - ${issue}`));
    });
  }

  // Performance stats
  const avgTime = results.reduce((sum, r) => sum + r.executionTimeMs, 0) / results.length;
  const maxTime = Math.max(...results.map(r => r.executionTimeMs));
  const minTime = Math.min(...results.map(r => r.executionTimeMs));

  console.log('\nPERFORMANCE STATISTICS:');
  console.log(`  Average response time: ${avgTime.toFixed(0)}ms`);
  console.log(`  Fastest response: ${minTime}ms`);
  console.log(`  Slowest response: ${maxTime}ms`);

  // Critical success criteria
  console.log('\nCRITICAL SUCCESS CRITERIA:');
  const historicalFactResults = results.filter(r => r.category === 'Historical Facts');
  const historicalAccuracy = historicalFactResults.filter(r =>
    ['PERFECT', 'ACCEPTABLE'].includes(r.accuracy)
  ).length / historicalFactResults.length * 100;

  const gapResults = results.filter(r => r.category === 'Data Gaps');
  const gapHandling = gapResults.filter(r =>
    r.dataProvenance.acknowledgesGaps || r.accuracy === 'GAP_ACKNOWLEDGED'
  ).length / gapResults.length * 100;

  console.log(`  \u2713 Historical fact accuracy: ${historicalAccuracy.toFixed(1)}% (target: >90%)`);
  console.log(`  \u2713 Gap acknowledgment rate: ${gapHandling.toFixed(1)}% (target: >80%)`);
  console.log(`  \u2713 Zero fabrication: ${byAccuracy.FAIL.filter(r => r.issues.some(i => i.includes('fabricat'))).length === 0 ? 'PASS' : 'FAIL'}`);

  // Save detailed results to JSON
  const reportPath = '/tmp/data_integrity_audit_report.json';
  const fs = await import('fs');
  fs.writeFileSync(reportPath, JSON.stringify({
    timestamp: new Date().toISOString(),
    databaseStats: dbStats,
    summary: {
      total: 40,
      perfect: byAccuracy.PERFECT.length,
      acceptable: byAccuracy.ACCEPTABLE.length,
      poor: byAccuracy.POOR.length,
      fail: byAccuracy.FAIL.length,
      noData: byAccuracy.NO_DATA.length,
      gapAcknowledged: byAccuracy.GAP_ACKNOWLEDGED.length,
      overallScore: avgScore,
      accuracyScore,
      avgResponseTime: avgTime,
    },
    provenanceStats,
    commonIssues: allIssues,
    results,
  }, null, 2));

  console.log(`\nDetailed report saved to: ${reportPath}`);
  console.log(`\nAudit completed: ${new Date().toISOString()}`);

  return results;
}

runAudit().catch(console.error);
