import { setActivePinia, createPinia } from 'pinia';
import { useNotificationStore, type Notification } from '../notifications'; // Adjust path if necessary
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';

describe('Notifications Store', () => {
  beforeEach(() => {
    setActivePinia(createPinia());
    vi.useFakeTimers(); // Use fake timers for setTimeout
  });

  afterEach(() => {
    vi.clearAllTimers(); // Clear all timers after each test
    vi.useRealTimers(); // Restore real timers
  });

  it('initializes with an empty notifications array', () => {
    const store = useNotificationStore();
    expect(store.notifications).toEqual([]);
  });

  describe('addNotification', () => {
    it('adds a new notification to the beginning of the array with a unique ID', () => {
      const store = useNotificationStore();
      const notificationDetails1 = { message: 'Test 1', type: 'success' as const };
      const notificationDetails2 = { message: 'Test 2', type: 'info' as const };

      store.addNotification(notificationDetails1);
      expect(store.notifications.length).toBe(1);
      expect(store.notifications[0].message).toBe('Test 1');
      expect(store.notifications[0].type).toBe('success');
      expect(store.notifications[0].id).toBeDefined();

      const firstId = store.notifications[0].id;

      store.addNotification(notificationDetails2);
      expect(store.notifications.length).toBe(2);
      expect(store.notifications[0].message).toBe('Test 2'); // Newest on top
      expect(store.notifications[0].type).toBe('info');
      expect(store.notifications[0].id).toBeDefined();
      expect(store.notifications[0].id).not.toBe(firstId); // Check for unique ID
    });

    it('generates unique IDs even when called rapidly', () => {
      const store = useNotificationStore();
      const ids = new Set<string>();
      for (let i = 0; i < 100; i++) {
        store.addNotification({ message: `Msg ${i}`, type: 'info' });
        ids.add(store.notifications[0].id);
      }
      expect(ids.size).toBe(100);
    });

    it('sets a timeout to remove notification if not manualClose', () => {
      const store = useNotificationStore();
      const notificationDetails = { message: 'Auto close', type: 'warning' as const };
      const removeNotificationSpy = vi.spyOn(store, 'removeNotification');

      store.addNotification(notificationDetails);
      const notificationId = store.notifications[0].id;

      expect(store.notifications.length).toBe(1);
      expect(setTimeout).toHaveBeenCalledTimes(1);
      expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), store.defaultDuration); // defaultDuration is not exported, so can't directly check value without hardcoding

      // Fast-forward time
      vi.runAllTimers();

      expect(removeNotificationSpy).toHaveBeenCalledWith(notificationId);
      // The actual removal is tested in 'removeNotification' tests, here we check the call.
      // To be fully self-contained, we could also check store.notifications.length here after timers run if removeNotification wasn't spied.
    });
    
    it('sets a timeout with custom duration if provided and not manualClose', () => {
        const store = useNotificationStore();
        const customDuration = 1000;
        const notificationDetails = { message: 'Custom duration', type: 'success' as const, duration: customDuration };
        vi.spyOn(store, 'removeNotification');
  
        store.addNotification(notificationDetails);
        expect(setTimeout).toHaveBeenCalledTimes(1);
        expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), customDuration);
  
        vi.runAllTimers();
        expect(store.removeNotification).toHaveBeenCalledWith(store.notifications[0]?.id || expect.anything()); // ID might be gone
      });

    it('does not set a timeout if manualClose is true', () => {
      const store = useNotificationStore();
      const notificationDetails = { message: 'Manual close', type: 'error' as const, manualClose: true };
      vi.spyOn(store, 'removeNotification');

      store.addNotification(notificationDetails);
      expect(store.notifications.length).toBe(1);
      expect(setTimeout).not.toHaveBeenCalled();

      // Fast-forward time to ensure no removal if timeout was accidentally set
      vi.runAllTimers();
      expect(store.removeNotification).not.toHaveBeenCalled();
    });
  });

  describe('removeNotification', () => {
    it('removes the specified notification by ID', () => {
      const store = useNotificationStore();
      const notification1: Notification = { id: 'id1', message: 'Test 1', type: 'success' };
      const notification2: Notification = { id: 'id2', message: 'Test 2', type: 'info' };
      store.notifications = [notification1, notification2]; // Manually set state for test

      store.removeNotification('id1');
      expect(store.notifications.length).toBe(1);
      expect(store.notifications[0].id).toBe('id2');
    });

    it('does nothing if the ID does not exist', () => {
      const store = useNotificationStore();
      const notification1: Notification = { id: 'id1', message: 'Test 1', type: 'success' };
      store.notifications = [notification1];

      store.removeNotification('nonExistentId');
      expect(store.notifications.length).toBe(1);
      expect(store.notifications[0].id).toBe('id1');
    });

    it('removes the correct notification when multiple exist', () => {
      const store = useNotificationStore();
      const notifications: Notification[] = [
        { id: 'id1', message: 'Msg 1', type: 'info' },
        { id: 'id2', message: 'Msg 2', type: 'success' },
        { id: 'id3', message: 'Msg 3', type: 'error' },
      ];
      store.notifications = [...notifications];

      store.removeNotification('id2');
      expect(store.notifications.map(n => n.id)).toEqual(['id1', 'id3']);

      store.removeNotification('id1');
      expect(store.notifications.map(n => n.id)).toEqual(['id3']);
      
      store.removeNotification('id3');
      expect(store.notifications).toEqual([]);
    });
  });

  describe('clearAllNotifications', () => {
    it('clears all notifications from the store', () => {
      const store = useNotificationStore();
      store.notifications = [
        { id: 'id1', message: 'Test 1', type: 'success' },
        { id: 'id2', message: 'Test 2', type: 'info' },
      ];

      store.clearAllNotifications();
      expect(store.notifications.length).toBe(0);
      expect(store.notifications).toEqual([]);
    });

    it('does nothing if notifications array is already empty', () => {
      const store = useNotificationStore();
      store.notifications = [];

      store.clearAllNotifications();
      expect(store.notifications.length).toBe(0);
    });
  });
});
