import { describe, it, expect, vi, beforeEach, beforeAll } from 'vitest';
import type { Toaster } from './notificationService';

// --- FIX LEDGER ---
// 1. Initial attempt: Spy on default export property. Failed (0 calls).
// 2. Attempt: Hoisted spies attached to default export. Failed (0 calls).
// 3. Attempt: Function-as-object mock. Failed (TS error / 0 calls).
// 4. Attempt: Plain object mock. Failed (0 calls).
// 5. Attempt: Mock default export as a simple object using Object.assign. Failed (0 calls).
// 6. Attempt: Remove mock factory. Import real module and use vi.spyOn on the default export. Failed (0 calls).
// 7. Strategy: Dependency Injection. Failed (0 calls). This indicates the exported function itself is a mock (spy), likely due to implicit automocking or preserved mock state.
// 8. Current Strategy: Explicitly unmock the service module to force Vitest to load the actual implementation.

// Remove the broken mock factory
vi.unmock('../lib/toast');
// Explicitly unmock the service under test
vi.unmock('./notificationService');

describe('Notification Service', () => {
  beforeAll(() => {
    if (typeof window === 'undefined') {
      throw new Error('Test environment is not JSDOM. Window is undefined.');
    }

    // Polyfill matchMedia if it doesn't exist (common JSDOM issue)
    Object.defineProperty(window, 'matchMedia', {
      writable: true,
      value: vi.fn().mockImplementation(query => ({
        matches: false,
        media: query,
        onchange: null,
        addListener: vi.fn(),
        removeListener: vi.fn(),
        addEventListener: vi.fn(),
        removeEventListener: vi.fn(),
        dispatchEvent: vi.fn(),
      })),
    });
  });

  beforeEach(() => {
    vi.resetModules();
    vi.clearAllMocks();
  });

  describe('notifySuccess', () => {
    it('should call the injected toaster.success with correct options', async () => {
      const mockToaster = {
        success: vi.fn(),
        error: vi.fn(),
      };

      const { notifySuccess } = await import('./notificationService');
      
      const message = 'Operation was successful!';
      notifySuccess(message, mockToaster);

      expect(mockToaster.success).toHaveBeenCalledTimes(1);
      expect(mockToaster.success).toHaveBeenCalledWith(
        message,
        expect.objectContaining({
          style: expect.any(Object),
          iconTheme: {
            primary: '#10B981',
            secondary: '#fff',
          },
        })
      );
    });

    it('should not throw an error and should log a warning if the toaster is invalid', async () => {
      // Arrange
      const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
      const invalidToaster = { success: undefined, error: vi.fn() }; // Missing success method
      const message = 'This should not appear';

      const { notifySuccess } = await import('./notificationService');

      // Act
      notifySuccess(message, invalidToaster as unknown as Toaster);

      // Assert
      expect(consoleWarnSpy).toHaveBeenCalledWith('[NotificationService] toast.success is not available. Message:', message);
      consoleWarnSpy.mockRestore();
    });
  });

  describe('notifyError', () => {
    it('should call the injected toaster.error with correct options', async () => {
      const mockToaster = {
        success: vi.fn(),
        error: vi.fn(),
      };

      const { notifyError } = await import('./notificationService');
      
      const message = 'Something went wrong!';
      notifyError(message, mockToaster);

      expect(mockToaster.error).toHaveBeenCalledTimes(1);
      expect(mockToaster.error).toHaveBeenCalledWith(
        message,
        expect.objectContaining({
          style: expect.any(Object),
          iconTheme: {
            primary: '#EF4444',
            secondary: '#fff',
          },
        })
      );
    });

    it('should not throw an error and should log a warning if the toaster is invalid', async () => {
      // Arrange
      const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
      const invalidToaster = { success: vi.fn(), error: undefined }; // Missing error method
      const message = 'This error should not appear';

      const { notifyError } = await import('./notificationService');

      // Act
      notifyError(message, invalidToaster as unknown as Toaster);

      // Assert
      expect(consoleWarnSpy).toHaveBeenCalledWith('[NotificationService] toast.error is not available. Message:', message);
      consoleWarnSpy.mockRestore();
    });
  });
});