/**
 * Tests for the export utility functions
 * @jest-environment jsdom
 */

import { exportToCSV, exportToJSON, formatDateForExport } from '@/utils/export';

describe('Export Utilities', () => {
  // Mock DOM methods for file download
  let mockClick: jest.Mock;
  let mockLink: Partial<HTMLAnchorElement>;
  const originalCreateObjectURL = URL.createObjectURL;
  const originalRevokeObjectURL = URL.revokeObjectURL;

  beforeAll(() => {
    // Mock URL methods globally
    URL.createObjectURL = jest.fn(() => 'blob:test-url');
    URL.revokeObjectURL = jest.fn();
  });

  afterAll(() => {
    URL.createObjectURL = originalCreateObjectURL;
    URL.revokeObjectURL = originalRevokeObjectURL;
  });

  beforeEach(() => {
    mockClick = jest.fn();
    mockLink = {
      href: '',
      download: '',
      style: { display: '' } as CSSStyleDeclaration,
      click: mockClick,
    };

    jest.spyOn(document, 'createElement').mockReturnValue(mockLink as HTMLAnchorElement);
    jest.spyOn(document.body, 'appendChild').mockImplementation(() => mockLink as Node);
    jest.spyOn(document.body, 'removeChild').mockImplementation(() => mockLink as Node);
  });

  afterEach(() => {
    jest.restoreAllMocks();
  });

  describe('exportToCSV', () => {
    it('should create and download a CSV file', () => {
      const data = [
        ['Header1', 'Header2'],
        ['Value1', 'Value2'],
      ];

      exportToCSV(data, 'test-file');

      expect(document.createElement).toHaveBeenCalledWith('a');
      expect(URL.createObjectURL).toHaveBeenCalled();
      expect(document.body.appendChild).toHaveBeenCalled();
      expect(mockClick).toHaveBeenCalled();
      expect(document.body.removeChild).toHaveBeenCalled();
      expect(URL.revokeObjectURL).toHaveBeenCalledWith('blob:test-url');
    });

    it('should include date in filename by default', () => {
      const data = [['test']];
      const today = new Date().toISOString().split('T')[0];

      exportToCSV(data, 'test-file');

      expect(mockLink.download).toContain('test-file');
      expect(mockLink.download).toContain(today);
      expect(mockLink.download).toContain('.csv');
    });

    it('should exclude date when includeDate is false', () => {
      const data = [['test']];

      exportToCSV(data, 'test-file', { includeDate: false });

      expect(mockLink.download).toBe('test-file.csv');
    });

    it('should use custom separator', () => {
      const data = [['A', 'B'], ['C', 'D']];

      // We can't easily verify the content of the blob, but we can verify the function runs
      expect(() => exportToCSV(data, 'test', { separator: ';' })).not.toThrow();
      expect(mockClick).toHaveBeenCalled();
    });

    it('should handle null and undefined values', () => {
      const data = [[null, undefined, 'value']];

      expect(() => exportToCSV(data, 'test')).not.toThrow();
      expect(mockClick).toHaveBeenCalled();
    });

    it('should escape values containing separator', () => {
      const data = [['value,with,commas', 'normal']];

      expect(() => exportToCSV(data, 'test')).not.toThrow();
      expect(mockClick).toHaveBeenCalled();
    });

    it('should escape values containing quotes', () => {
      const data = [['value"with"quotes', 'normal']];

      expect(() => exportToCSV(data, 'test')).not.toThrow();
      expect(mockClick).toHaveBeenCalled();
    });

    it('should escape values containing newlines', () => {
      const data = [['value\nwith\nnewlines', 'normal']];

      expect(() => exportToCSV(data, 'test')).not.toThrow();
      expect(mockClick).toHaveBeenCalled();
    });

    it('should handle boolean values', () => {
      const data = [[true, false]];

      expect(() => exportToCSV(data, 'test')).not.toThrow();
      expect(mockClick).toHaveBeenCalled();
    });

    it('should handle number values', () => {
      const data = [[123, 456.78]];

      expect(() => exportToCSV(data, 'test')).not.toThrow();
      expect(mockClick).toHaveBeenCalled();
    });

    it('should set link style to hidden', () => {
      const data = [['test']];

      exportToCSV(data, 'test');

      expect(mockLink.style?.display).toBe('none');
    });
  });

  describe('exportToJSON', () => {
    it('should create and download a JSON file', () => {
      const data = { key: 'value', nested: { a: 1 } };

      exportToJSON(data, 'test-file');

      expect(document.createElement).toHaveBeenCalledWith('a');
      expect(URL.createObjectURL).toHaveBeenCalled();
      expect(document.body.appendChild).toHaveBeenCalled();
      expect(mockClick).toHaveBeenCalled();
      expect(document.body.removeChild).toHaveBeenCalled();
      expect(URL.revokeObjectURL).toHaveBeenCalledWith('blob:test-url');
    });

    it('should include date in filename', () => {
      const data = { test: true };
      const today = new Date().toISOString().split('T')[0];

      exportToJSON(data, 'test-file');

      expect(mockLink.download).toContain('test-file');
      expect(mockLink.download).toContain(today);
      expect(mockLink.download).toContain('.json');
    });

    it('should handle arrays', () => {
      const data = [1, 2, 3, { a: 'b' }];

      expect(() => exportToJSON(data, 'test')).not.toThrow();
      expect(mockClick).toHaveBeenCalled();
    });

    it('should handle nested objects', () => {
      const data = {
        level1: {
          level2: {
            level3: 'deep',
          },
        },
      };

      expect(() => exportToJSON(data, 'test')).not.toThrow();
      expect(mockClick).toHaveBeenCalled();
    });

    it('should set link style to hidden', () => {
      const data = { test: true };

      exportToJSON(data, 'test');

      expect(mockLink.style?.display).toBe('none');
    });
  });

  describe('formatDateForExport', () => {
    it('should format date string correctly', () => {
      const result = formatDateForExport('2024-01-15T10:30:00Z');

      // French format: DD/MM/YYYY HH:MM
      expect(result).toMatch(/\d{2}\/\d{2}\/\d{4}/);
    });

    it('should format Date object correctly', () => {
      const date = new Date('2024-01-15T10:30:00Z');
      const result = formatDateForExport(date);

      expect(result).toMatch(/\d{2}\/\d{2}\/\d{4}/);
    });

    it('should return empty string for null', () => {
      expect(formatDateForExport(null)).toBe('');
    });

    it('should return empty string for undefined', () => {
      expect(formatDateForExport(undefined)).toBe('');
    });

    it('should return empty string for empty string', () => {
      expect(formatDateForExport('')).toBe('');
    });

    it('should include time in the format', () => {
      const result = formatDateForExport('2024-01-15T10:30:00Z');

      // Should contain time component
      expect(result).toMatch(/\d{2}:\d{2}/);
    });
  });
});
