#!/usr/bin/env node
/* eslint-env node */
/* global console, process */

/**
 * Bundle size monitoring script
 * Analyzes bundle size and reports on performance metrics
 */

import { readFileSync, writeFileSync, existsSync } from "fs";
import { join, dirname } from "path";
import { fileURLToPath } from "url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const BUNDLE_SIZE_LIMIT = {
  // Size limits in KB
  total: 1000, // 1MB total
  individual: 250, // 250KB per chunk
  vendor: 500, // 500KB for vendor chunks
};

// Note: Performance budgets can be added here when integrated

class BundleMonitor {
  constructor() {
    this.projectRoot = join(__dirname, "..");
    this.buildDir = join(this.projectRoot, ".next");
    this.reportFile = join(this.projectRoot, "bundle-report.json");
  }

  /**
   * Analyze bundle size from Next.js build output
   */
  analyzeBundleSize() {
    const buildManifest = join(this.buildDir, "build-manifest.json");

    if (!existsSync(buildManifest)) {
      console.error('Build manifest not found. Run "npm run build" first.');
      process.exit(1);
    }

    try {
      const manifest = JSON.parse(readFileSync(buildManifest, "utf8"));
      const chunks = [];
      let totalSize = 0;

      // Analyze JavaScript chunks
      Object.entries(manifest.pages || {}).forEach(([page, files]) => {
        files.forEach(file => {
          if (file.endsWith(".js")) {
            const filePath = join(this.buildDir, "static", file);
            if (existsSync(filePath)) {
              const stats = this.getFileStats(filePath);
              chunks.push({
                page,
                file,
                size: stats.size,
                gzippedSize: stats.gzippedSize,
              });
              totalSize += stats.size;
            }
          }
        });
      });

      return {
        totalSize,
        chunks,
        timestamp: Date.now(),
      };
    } catch (error) {
      console.error("Error analyzing bundle:", error.message);
      process.exit(1);
    }
  }

  /**
   * Get file statistics including gzipped size
   */
  getFileStats(filePath) {
    try {
      const content = readFileSync(filePath);
      const size = content.length;

      // Estimate gzipped size (rough approximation)
      const gzippedSize = Math.round(size * 0.3); // Typical compression ratio

      return { size, gzippedSize };
    } catch {
      return { size: 0, gzippedSize: 0 };
    }
  }

  /**
   * Check if bundle sizes are within limits
   */
  checkBundleLimits(analysis) {
    const issues = [];

    // Check total size
    const totalSizeKB = analysis.totalSize / 1024;
    if (totalSizeKB > BUNDLE_SIZE_LIMIT.total) {
      issues.push({
        type: "total_size",
        message: `Total bundle size (${totalSizeKB.toFixed(1)}KB) exceeds limit (${BUNDLE_SIZE_LIMIT.total}KB)`,
        severity: "error",
      });
    }

    // Check individual chunks
    analysis.chunks.forEach(chunk => {
      const sizeKB = chunk.size / 1024;

      if (sizeKB > BUNDLE_SIZE_LIMIT.individual) {
        const isVendor = chunk.file.includes("vendor") || chunk.file.includes("node_modules");
        const limit = isVendor ? BUNDLE_SIZE_LIMIT.vendor : BUNDLE_SIZE_LIMIT.individual;

        if (sizeKB > limit) {
          issues.push({
            type: "chunk_size",
            message: `Chunk ${chunk.file} (${sizeKB.toFixed(1)}KB) exceeds limit (${limit}KB)`,
            severity: "warning",
            chunk: chunk.file,
          });
        }
      }
    });

    return issues;
  }

  /**
   * Generate recommendations for bundle optimization
   */
  generateRecommendations(analysis) {
    const recommendations = [];

    // Large chunks recommendations
    const largeChunks = analysis.chunks
      .filter(chunk => chunk.size / 1024 > 100)
      .sort((a, b) => b.size - a.size);

    if (largeChunks.length > 0) {
      recommendations.push({
        type: "code_splitting",
        message: "Consider implementing code splitting for large chunks",
        chunks: largeChunks.slice(0, 5).map(c => c.file),
      });
    }

    // Vendor chunk recommendations
    const vendorChunks = analysis.chunks.filter(
      chunk => chunk.file.includes("vendor") || chunk.file.includes("framework")
    );

    if (vendorChunks.some(chunk => chunk.size / 1024 > 300)) {
      recommendations.push({
        type: "vendor_optimization",
        message: "Consider optimizing vendor chunks or using dynamic imports",
      });
    }

    // Duplicate code detection (simplified)
    const pageChunks = analysis.chunks.filter(chunk => chunk.page !== "_app");
    if (pageChunks.length > 10) {
      recommendations.push({
        type: "common_chunks",
        message: "Consider extracting common code into shared chunks",
      });
    }

    return recommendations;
  }

  /**
   * Load previous report for comparison
   */
  loadPreviousReport() {
    if (existsSync(this.reportFile)) {
      try {
        return JSON.parse(readFileSync(this.reportFile, "utf8"));
      } catch (error) {
        console.warn("Could not load previous report:", error.message);
      }
    }
    return null;
  }

  /**
   * Save current report
   */
  saveReport(report) {
    try {
      writeFileSync(this.reportFile, JSON.stringify(report, null, 2));
      console.log(`Report saved to ${this.reportFile}`);
    } catch (error) {
      console.error("Could not save report:", error.message);
    }
  }

  /**
   * Compare with previous report
   */
  compareWithPrevious(current, previous) {
    if (!previous) return null;

    const currentTotal = current.analysis.totalSize;
    const previousTotal = previous.analysis.totalSize;
    const sizeDiff = currentTotal - previousTotal;
    const percentChange = (sizeDiff / previousTotal) * 100;

    return {
      sizeDiff,
      percentChange,
      isRegression: sizeDiff > 10240, // 10KB threshold
    };
  }

  /**
   * Generate and display report
   */
  run() {
    console.log("🔍 Analyzing bundle size...\n");

    const analysis = this.analyzeBundleSize();
    const issues = this.checkBundleLimits(analysis);
    const recommendations = this.generateRecommendations(analysis);
    const previous = this.loadPreviousReport();
    const comparison = this.compareWithPrevious({ analysis }, previous);

    const report = {
      timestamp: Date.now(),
      analysis,
      issues,
      recommendations,
      comparison,
    };

    // Display results
    this.displayReport(report);

    // Save report
    this.saveReport(report);

    // Exit with error code if there are critical issues
    const hasErrors = issues.some(issue => issue.severity === "error");
    if (hasErrors) {
      console.log("\n❌ Bundle analysis failed due to critical issues.");
      process.exit(1);
    } else {
      console.log("\n✅ Bundle analysis completed successfully.");
    }
  }

  /**
   * Display formatted report
   */
  displayReport(report) {
    const { analysis, issues, recommendations, comparison } = report;

    // Bundle size summary
    console.log("📊 Bundle Size Summary");
    console.log("─".repeat(50));
    console.log(`Total Size: ${(analysis.totalSize / 1024).toFixed(1)}KB`);
    console.log(`Chunks: ${analysis.chunks.length}`);

    if (comparison) {
      const sign = comparison.sizeDiff > 0 ? "+" : "";
      const color = comparison.isRegression ? "\x1b[31m" : "\x1b[32m";
      console.log(
        `Change: ${color}${sign}${(comparison.sizeDiff / 1024).toFixed(1)}KB (${comparison.percentChange.toFixed(1)}%)\x1b[0m`
      );
    }

    // Top chunks
    console.log("\n📦 Largest Chunks");
    console.log("─".repeat(50));
    analysis.chunks
      .sort((a, b) => b.size - a.size)
      .slice(0, 10)
      .forEach(chunk => {
        console.log(`${(chunk.size / 1024).toFixed(1)}KB - ${chunk.file}`);
      });

    // Issues
    if (issues.length > 0) {
      console.log("\n⚠️  Issues Found");
      console.log("─".repeat(50));
      issues.forEach(issue => {
        const icon = issue.severity === "error" ? "❌" : "⚠️ ";
        console.log(`${icon} ${issue.message}`);
      });
    }

    // Recommendations
    if (recommendations.length > 0) {
      console.log("\n💡 Recommendations");
      console.log("─".repeat(50));
      recommendations.forEach(rec => {
        console.log(`• ${rec.message}`);
        if (rec.chunks) {
          rec.chunks.forEach(chunk => console.log(`  - ${chunk}`));
        }
      });
    }
  }
}

// Run the monitor
const monitor = new BundleMonitor();
monitor.run();
