/**
 * @fileoverview Tests for the asset optimizer
 */

import { describe, expect, it, vi } from 'vitest';
import { AssetOptimizer } from './asset-optimizer.js';
import * as fs from './fs.js';

// Mock the fs module
vi.mock('./fs.js', () => ({
  readFile: vi.fn(),
  writeFile: vi.fn(),
  mkdir: vi.fn()
}));

// Mock the terser module
vi.mock('terser', () => ({
  Terser: {
    minify: vi.fn()
  }
}));

// Mock the brotli module
vi.mock('brotli', () => ({
  compress: vi.fn()
}));

// Mock the gzip-size module
vi.mock('gzip-size', () => ({
  default: vi.fn()
}));

describe('AssetOptimizer', () => {
  beforeEach(() => {
    vi.resetAllMocks();
  });

  describe('constructor', () => {
    it('should create an instance with default options', () => {
      const optimizer = new AssetOptimizer();
      expect(optimizer).toBeDefined();
    });

    it('should create an instance with custom options', () => {
      const optimizer = new AssetOptimizer({
        minifyJs: false,
        minifyCss: false,
        brotli: false,
        gzip: false
      });
      expect(optimizer).toBeDefined();
    });
  });

  describe('optimizeJavaScript', () => {
    it('should optimize JavaScript files', async () => {
      // Mock the readFile function
      vi.mocked(fs.readFile).mockResolvedValue('function test() { console.log("test"); }');

      // Mock the Terser.minify function
      vi.mocked(require('terser').Terser.minify).mockResolvedValue({
        code: 'function test(){console.log("test")}',
        map: undefined
      });

      // Mock the gzip-size function
      vi.mocked(require('gzip-size').default).mockResolvedValue(30);

      // Mock the brotli.compress function
      vi.mocked(require('brotli').compress).mockReturnValue(Buffer.from('compressed'));

      const optimizer = new AssetOptimizer({
        minifyJs: true,
        minifyCss: true,
        brotli: true,
        gzip: true
      });

      const result = await optimizer.optimizeJavaScript('test.js');

      expect(result).toBeDefined();
      expect(result.filePath).toBe('test.js');
      expect(result.type).toBe('js');
      expect(result.minified).toBe(true);
      expect(result.gzipped).toBe(true);
      expect(result.brotlified).toBe(true);
      expect(result.sizes.originalSize).toBeGreaterThan(0);
      expect(result.sizes.minifiedSize).toBeGreaterThan(0);
      expect(result.sizes.gzipSize).toBe(30);
      expect(result.sizes.brotliSize).toBe(10); // Length of 'compressed'
    });
  });

  describe('optimizeCSS', () => {
    it('should optimize CSS files', async () => {
      // Mock the readFile function
      vi.mocked(fs.readFile).mockResolvedValue('body { color: red; }');

      // Mock the gzip-size function
      vi.mocked(require('gzip-size').default).mockResolvedValue(20);

      // Mock the brotli.compress function
      vi.mocked(require('brotli').compress).mockReturnValue(Buffer.from('compressed'));

      const optimizer = new AssetOptimizer({
        minifyJs: true,
        minifyCss: true,
        brotli: true,
        gzip: true
      });

      const result = await optimizer.optimizeCSS('test.css');

      expect(result).toBeDefined();
      expect(result.filePath).toBe('test.css');
      expect(result.type).toBe('css');
      expect(result.minified).toBe(true);
      expect(result.gzipped).toBe(true);
      expect(result.brotlified).toBe(true);
      expect(result.sizes.originalSize).toBeGreaterThan(0);
      expect(result.sizes.gzipSize).toBe(20);
      expect(result.sizes.brotliSize).toBe(10); // Length of 'compressed'
    });
  });

  describe('optimizeHTML', () => {
    it('should optimize HTML files', async () => {
      // Mock the readFile function
      vi.mocked(fs.readFile).mockResolvedValue('<!DOCTYPE html><html><body>Test</body></html>');

      // Mock the gzip-size function
      vi.mocked(require('gzip-size').default).mockResolvedValue(25);

      // Mock the brotli.compress function
      vi.mocked(require('brotli').compress).mockReturnValue(Buffer.from('compressed'));

      const optimizer = new AssetOptimizer({
        minifyJs: true,
        minifyCss: true,
        brotli: true,
        gzip: true
      });

      const result = await optimizer.optimizeHTML('test.html');

      expect(result).toBeDefined();
      expect(result.filePath).toBe('test.html');
      expect(result.type).toBe('html');
      expect(result.minified).toBe(true);
      expect(result.gzipped).toBe(true);
      expect(result.brotlified).toBe(true);
      expect(result.sizes.originalSize).toBeGreaterThan(0);
      expect(result.sizes.gzipSize).toBe(25);
      expect(result.sizes.brotliSize).toBe(10); // Length of 'compressed'
    });
  });

  describe('optimizeDirectory', () => {
    it('should optimize all files in a directory', async () => {
      // Mock the glob function
      vi.mock('glob', () => ({
        default: vi.fn().mockResolvedValue(['test.js', 'test.css', 'test.html'])
      }));

      // Create a spy on the optimizer methods
      const optimizer = new AssetOptimizer();
      const jsOptimizeSpy = vi.spyOn(optimizer, 'optimizeJavaScript').mockResolvedValue({
        filePath: 'test.js',
        type: 'js',
        sizes: {
          originalSize: 100,
          minifiedSize: 50,
          gzipSize: 30,
          brotliSize: 20,
          compressionRatio: 0.5,
          originalSizeHuman: '100 B',
          minifiedSizeHuman: '50 B',
          gzipSizeHuman: '30 B',
          brotliSizeHuman: '20 B'
        },
        minified: true,
        gzipped: true,
        brotlified: true
      });

      const cssOptimizeSpy = vi.spyOn(optimizer, 'optimizeCSS').mockResolvedValue({
        filePath: 'test.css',
        type: 'css',
        sizes: {
          originalSize: 80,
          minifiedSize: 40,
          gzipSize: 25,
          brotliSize: 15,
          compressionRatio: 0.5,
          originalSizeHuman: '80 B',
          minifiedSizeHuman: '40 B',
          gzipSizeHuman: '25 B',
          brotliSizeHuman: '15 B'
        },
        minified: true,
        gzipped: true,
        brotlified: true
      });

      const htmlOptimizeSpy = vi.spyOn(optimizer, 'optimizeHTML').mockResolvedValue({
        filePath: 'test.html',
        type: 'html',
        sizes: {
          originalSize: 120,
          minifiedSize: 60,
          gzipSize: 35,
          brotliSize: 25,
          compressionRatio: 0.5,
          originalSizeHuman: '120 B',
          minifiedSizeHuman: '60 B',
          gzipSizeHuman: '35 B',
          brotliSizeHuman: '25 B'
        },
        minified: true,
        gzipped: true,
        brotlified: true
      });

      const results = await optimizer.optimizeDirectory('dist');

      expect(results).toBeDefined();
      expect(results.assets.length).toBe(3);
      expect(results.totalOriginalSize).toBe(300);
      expect(results.totalMinifiedSize).toBe(150);
      expect(results.totalGzipSize).toBe(90);
      expect(results.totalBrotliSize).toBe(60);
      expect(results.overallCompressionRatio).toBe(0.5);

      expect(jsOptimizeSpy).toHaveBeenCalledWith('test.js');
      expect(cssOptimizeSpy).toHaveBeenCalledWith('test.css');
      expect(htmlOptimizeSpy).toHaveBeenCalledWith('test.html');
    });
  });

  describe('generateSizeReport', () => {
    it('should generate a size report', () => {
      const optimizer = new AssetOptimizer();
      const results = {
        assets: [
          {
            filePath: 'test.js',
            type: 'js',
            sizes: {
              originalSize: 100,
              minifiedSize: 50,
              gzipSize: 30,
              brotliSize: 20,
              compressionRatio: 0.5,
              originalSizeHuman: '100 B',
              minifiedSizeHuman: '50 B',
              gzipSizeHuman: '30 B',
              brotliSizeHuman: '20 B'
            },
            minified: true,
            gzipped: true,
            brotlified: true
          },
          {
            filePath: 'test.css',
            type: 'css',
            sizes: {
              originalSize: 80,
              minifiedSize: 40,
              gzipSize: 25,
              brotliSize: 15,
              compressionRatio: 0.5,
              originalSizeHuman: '80 B',
              minifiedSizeHuman: '40 B',
              gzipSizeHuman: '25 B',
              brotliSizeHuman: '15 B'
            },
            minified: true,
            gzipped: true,
            brotlified: true
          }
        ],
        totalOriginalSize: 180,
        totalMinifiedSize: 90,
        totalGzipSize: 55,
        totalBrotliSize: 35,
        overallCompressionRatio: 0.5,
        totalOriginalSizeHuman: '180 B',
        totalMinifiedSizeHuman: '90 B',
        totalGzipSizeHuman: '55 B',
        totalBrotliSizeHuman: '35 B'
      };

      const report = optimizer.generateSizeReport(results);

      expect(report).toBeDefined();
      expect(report).toContain('Asset Size Report');
      expect(report).toContain('Total Original Size: 180 B');
      expect(report).toContain('Total Minified Size: 90 B (50% reduction)');
      expect(report).toContain('Total Gzip Size: 55 B');
      expect(report).toContain('Total Brotli Size: 35 B');
      expect(report).toContain('JavaScript Assets:');
      expect(report).toContain('CSS Assets:');
    });
  });
});
