// #region SidebarTest [C:3] [TYPE Module] [SEMANTICS sidebar, store, test, navigation, toggle]
// @BRIEF Unit tests for sidebar store validating expansion toggles, active item selection, and mobile open/close transitions.
// @RELATION DEPENDS_ON -> [EXT:frontend:sidebar]

// #region SidebarTest:Module [TYPE Function]
// @SEMANTICS: sidebar, store, tests, mobile, navigation
// @PURPOSE: Unit tests for sidebar store
// @LAYER Tests
// @RELATION DEPENDS_ON -> [EXT:frontend:sidebar]
// @INVARIANT: Sidebar store transitions must be deterministic across desktop/mobile toggles.

import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { get } from 'svelte/store';
import { loadState, sidebarStore, toggleSidebar, setActiveItem, setMobileOpen, closeMobile, toggleMobileSidebar } from '../sidebar.svelte.js';

// Mock the $app/environment module
const mockBrowser = vi.hoisted(() => ({ browser: false }));
vi.mock('$app/environment', () => mockBrowser);

describe('SidebarStore', () => {
  beforeEach(() => {
    mockBrowser.browser = false;
    localStorage.clear();
    // Reset sidebar to default state via available functions
    setActiveItem('dashboards', '/dashboards');
    closeMobile();
    // isExpanded defaults to true; if previous test toggled it, toggle back
    if (sidebarStore.value && !sidebarStore.value.isExpanded) {
      toggleSidebar();
    }
  });

  // #region test_sidebar_initial_state:Function [TYPE Function]
  // @PURPOSE: Verify initial sidebar store values when no persisted state is available.
  // @TEST: Store initializes with default values
  // @PRE: No localStorage state
  // @POST: Default state is { isExpanded: true, activeCategory: 'dashboards', activeItem: '/dashboards', isMobileOpen: false }
  describe('initial state', () => {
    it('should have default values when no localStorage', () => {
      const state = get(sidebarStore);

      expect(state.isExpanded).toBe(true);
      expect(state.activeCategory).toBe('dashboards');
      expect(state.activeItem).toBe('/dashboards');
      expect(state.isMobileOpen).toBe(false);
    });
  });
  // #endregion test_sidebar_initial_state:Function

  // #region test_toggleSidebar:Function [TYPE Function]
  // @PURPOSE: Verify desktop sidebar expansion toggles deterministically.
  // @TEST: toggleSidebar toggles isExpanded state
  // @PRE: Store is initialized
  // @POST: isExpanded is toggled from previous value
  describe('toggleSidebar', () => {
    it('should toggle isExpanded from true to false', () => {
      const initialState = get(sidebarStore);
      expect(initialState.isExpanded).toBe(true);

      toggleSidebar();

      const newState = get(sidebarStore);
      expect(newState.isExpanded).toBe(false);
    });

    it('should toggle isExpanded from false to true', () => {
      toggleSidebar(); // Now false
      toggleSidebar(); // Should be true again

      const state = get(sidebarStore);
      expect(state.isExpanded).toBe(true);
    });
  });
  // #endregion test_toggleSidebar:Function

  // #region test_setActiveItem:Function [TYPE Function]
// @PURPOSE: Verify active-category updates remain deterministic after item selection.
  // @TEST: setActiveItem updates activeCategory and activeItem
  // @PRE: Store is initialized
  // @POST: activeCategory and activeItem are updated
  describe('setActiveItem', () => {
    it('should update activeCategory and activeItem', () => {
      setActiveItem('datasets', '/datasets');

      const state = get(sidebarStore);
      expect(state.activeCategory).toBe('datasets');
      expect(state.activeItem).toBe('/datasets');
    });

    it('should update to admin category', () => {
      setActiveItem('admin', '/settings');

      const state = get(sidebarStore);
      expect(state.activeCategory).toBe('admin');
      expect(state.activeItem).toBe('/settings');
    });
  });
  // #endregion test_setActiveItem:Function

  // #region test_mobile_functions:Function [TYPE Function]
// @PURPOSE: Verify mobile sidebar helpers update open-state transitions predictably.
  // @TEST: Mobile functions correctly update isMobileOpen
  // @PRE: Store is initialized
  // @POST: isMobileOpen is correctly updated
  describe('mobile functions', () => {
    it('should set isMobileOpen to true with setMobileOpen', () => {
      setMobileOpen(true);

      const state = get(sidebarStore);
      expect(state.isMobileOpen).toBe(true);
    });

    it('should set isMobileOpen to false with closeMobile', () => {
      setMobileOpen(true);
      closeMobile();

      const state = get(sidebarStore);
      expect(state.isMobileOpen).toBe(false);
    });

    it('should toggle isMobileOpen with toggleMobileSidebar', () => {
      const initialState = get(sidebarStore);
      const initialMobileOpen = initialState.isMobileOpen;

      toggleMobileSidebar();

      const state1 = get(sidebarStore);
      expect(state1.isMobileOpen).toBe(!initialMobileOpen);

      toggleMobileSidebar();

      const state2 = get(sidebarStore);
      expect(state2.isMobileOpen).toBe(initialMobileOpen);
    });
  });
  // #endregion test_mobile_functions:Function

  // #region test_browser_mode:Function [TYPE Function]
  // @PURPOSE: Verify localStorage persistence works when browser=true
  describe('browser mode with localStorage', () => {
    it('should save state to localStorage when browser=true and restore it', () => {
      mockBrowser.browser = true;

      // Toggle sidebar and check persistence
      toggleSidebar();
      expect(sidebarStore.value.isExpanded).toBe(false);

      const saved = localStorage.getItem('sidebar_state');
      expect(saved).not.toBeNull();
      const parsed = JSON.parse(saved!);
      expect(parsed.isExpanded).toBe(false);
    });

    it('should restore saved state via loadState() with browser=true', () => {
      mockBrowser.browser = true;
      const testState = {
        isExpanded: false,
        activeCategory: 'settings',
        activeItem: '/settings',
        isMobileOpen: true,
      };
      localStorage.setItem('sidebar_state', JSON.stringify(testState));

      const result = loadState();
      expect(result).toEqual(testState);
    });

    it('should restore state from localStorage on import', async () => {
      mockBrowser.browser = true;

      // Save some state to localStorage
      localStorage.setItem('sidebar_state', JSON.stringify({
        isExpanded: false,
        activeCategory: 'datasets',
        activeItem: '/datasets',
        isMobileOpen: true,
      }));

      // Re-import to trigger loadState
      vi.resetModules();
      const freshMod = await import('../sidebar.svelte.js');
      const freshState = get(freshMod.sidebarStore);
      expect(freshState.isExpanded).toBe(false);
      expect(freshState.activeCategory).toBe('datasets');
      expect(freshState.activeItem).toBe('/datasets');
      expect(freshState.isMobileOpen).toBe(true);
    });

    it('should handle localStorage.getItem error gracefully', () => {
      mockBrowser.browser = true;
      const getItemSpy = vi.spyOn(Storage.prototype, 'getItem').mockImplementation(() => {
        throw new Error('Storage error');
      });

      // Re-import to trigger loadState with error
      // Since we can't control loadState via import, we test that the store still works
      const state = get(sidebarStore);
      expect(state.isExpanded).toBe(true);

      getItemSpy.mockRestore();
    });

    it('should handle localStorage.setItem error gracefully', () => {
      mockBrowser.browser = true;
      const origSetItem = localStorage.setItem;
      localStorage.setItem = vi.fn(() => { throw new Error('Storage full'); });

      // Should not throw
      toggleSidebar();

      localStorage.setItem = origSetItem;
    });

    it('should handle localStorage.getItem error gracefully via loadState', async () => {
      mockBrowser.browser = true;
      // Put bad JSON in localStorage to trigger JSON.parse error
      localStorage.setItem('sidebar_state', 'not-valid-json{{{');

      // Re-import to trigger loadState with bad JSON
      vi.resetModules();
      const freshMod = await import('../sidebar.svelte.js');
      const freshState = freshMod.sidebarStore.value;

      // Should recover with defaults
      expect(freshState.isExpanded).toBe(true);
      expect(freshState.activeCategory).toBe('dashboards');
      expect(freshState.activeItem).toBe('/dashboards');
      expect(freshState.isMobileOpen).toBe(false);
    });

    it('should handle localStorage.getItem throwing during loadState', () => {
      mockBrowser.browser = true;
      const getItemSpy = vi.spyOn(Storage.prototype, 'getItem').mockImplementation(() => {
        throw new Error('Storage unavailable');
      });

      // Re-import triggers loadState with getItem throwing immediately
      // The store should still be usable
      expect(() => {
        const state = sidebarStore.value;
        expect(state.isExpanded).toBe(true);
      }).not.toThrow();

      getItemSpy.mockRestore();
    });

    it('should restore from localStorage with valid saved state on re-import', async () => {
      mockBrowser.browser = true;
      // Store valid state
      localStorage.setItem('sidebar_state', JSON.stringify({
        isExpanded: false,
        activeCategory: 'settings',
        activeItem: '/settings',
        isMobileOpen: true,
      }));

      // This forces loadState to run with a valid saved string
      // -> if(saved) is true, JSON.parse succeeds -> returns parsed state
      vi.resetModules();
      const freshMod = await import('../sidebar.svelte.js');
      const state = freshMod.sidebarStore.value;
      expect(state.isExpanded).toBe(false);
      expect(state.activeCategory).toBe('settings');
      expect(state.activeItem).toBe('/settings');
      expect(state.isMobileOpen).toBe(true);
    });
  });
  // #endregion test_browser_mode:Function

  // #region test_edge_case_guards:Function [TYPE Function]
  describe('edge case guards', () => {
    it('should verify loadState called during initialization', async () => {
      // loadState() is called at module import time.
      // With browser=true and saved state in localStorage, loadState()
      // reads and returns the parsed state.
      // This test verifies the store reflects defaults when no state is saved.
      mockBrowser.browser = true;
      localStorage.removeItem('sidebar_state');

      // Re-import triggers loadState: localStorage.getItem returns null,
      // if(saved) is false, falls through to return null → defaults used
      vi.resetModules();
      // Use dynamic import to force module re-init
      const { sidebarStore } = await import('../sidebar.svelte.js');
      const state = sidebarStore.value;
      expect(state.isExpanded).toBe(true);
      expect(state.activeCategory).toBe('dashboards');
      expect(state.activeItem).toBe('/dashboards');
      expect(state.isMobileOpen).toBe(false);
    });

    it('setActiveItem with same values should be a no-op', () => {
      setActiveItem('dashboards', '/dashboards');
      // State should not have changed
      expect(sidebarStore.value.activeCategory).toBe('dashboards');
      expect(sidebarStore.value.activeItem).toBe('/dashboards');
    });

    it('setMobileOpen with same value should be a no-op', () => {
      setMobileOpen(false);
      expect(sidebarStore.value.isMobileOpen).toBe(false);
    });

    it('closeMobile when already closed should be a no-op', () => {
      // Mobile starts closed
      closeMobile();
      expect(sidebarStore.value.isMobileOpen).toBe(false);
      // Toggle open then close
      setMobileOpen(true);
      closeMobile();
      expect(sidebarStore.value.isMobileOpen).toBe(false);
      // Second close is a no-op
      closeMobile();
      expect(sidebarStore.value.isMobileOpen).toBe(false);
    });

    it('should notify active subscribers on state change', () => {
      // Keep subscription alive while mutating so _notify's forEach
      // callback body is exercised (previously uncovered when all
      // subscribers were cleaned up by get() before any mutation).
      const subscriber = vi.fn();
      const unsubscribe = sidebarStore.subscribe(subscriber);
      expect(subscriber).toHaveBeenCalledTimes(1); // initial value

      subscriber.mockClear();
      toggleSidebar();
      expect(subscriber).toHaveBeenCalledTimes(1);
      expect(subscriber).toHaveBeenCalledWith(expect.objectContaining({ isExpanded: false }));

      subscriber.mockClear();
      setActiveItem('datasets', '/datasets');
      expect(subscriber).toHaveBeenCalledTimes(1);
      expect(subscriber).toHaveBeenCalledWith(expect.objectContaining({ activeCategory: 'datasets' }));

      unsubscribe();
    });
  });
  // #endregion test_edge_case_guards:Function
});

// #endregion SidebarTest:Module
// #endregion SidebarTest
