/**
 * useLayoutState Hook Tests
 *
 * Tests for the useLayoutState custom hook that manages editor layout state.
 */

import React from 'react';
import { render, screen, fireEvent, act } from '@testing-library/react';
import '@testing-library/jest-dom';
import { useLayoutState } from '../useLayoutState';
import { PREVIEW_MODES, LAYOUT } from '../../constants';

// Test wrapper component
const TestComponent = ({ initialLayout, onStateChange }) => {
  const layoutState = useLayoutState(initialLayout);

  React.useEffect(() => {
    if (onStateChange) {
      onStateChange(layoutState);
    }
  });

  return (
    <div>
      <div data-testid="split-size">{layoutState.splitSize}</div>
      <div data-testid="view-mode">{layoutState.viewMode}</div>
      <div data-testid="mobile-width">{layoutState.mobileWidth}</div>
      <div data-testid="is-fullscreen">{String(layoutState.isFullscreen)}</div>
      <div data-testid="is-resizing">{String(layoutState.isResizing)}</div>
      <div data-testid="is-mobile-view">{String(layoutState.isMobileView)}</div>
      <div data-testid="is-desktop-view">{String(layoutState.isDesktopView)}</div>

      <button onClick={() => layoutState.setSplitSize(60)} data-testid="set-split">
        Set Split
      </button>
      <button onClick={() => layoutState.setViewMode(PREVIEW_MODES.MOBILE)} data-testid="set-mobile">
        Set Mobile
      </button>
      <button onClick={() => layoutState.toggleViewMode()} data-testid="toggle-view">
        Toggle View
      </button>
      <button onClick={() => layoutState.setMobileWidth(400)} data-testid="set-width">
        Set Width
      </button>
      <button onClick={() => layoutState.toggleFullscreen()} data-testid="toggle-fullscreen">
        Toggle Fullscreen
      </button>
      <button onClick={() => layoutState.resetLayout()} data-testid="reset">
        Reset
      </button>
      <button onClick={() => layoutState.setResizingState(true)} data-testid="start-resize">
        Start Resize
      </button>
      <button onClick={() => layoutState.updateSplitSizes([40, 60])} data-testid="update-sizes">
        Update Sizes
      </button>
    </div>
  );
};

describe('useLayoutState', () => {
  beforeEach(() => {
    // Clear any existing event listeners
    jest.clearAllMocks();
  });

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

  describe('Initial State', () => {
    it('initializes with default layout values', () => {
      render(<TestComponent />);

      expect(screen.getByTestId('split-size')).toHaveTextContent('50');
      expect(screen.getByTestId('view-mode')).toHaveTextContent(PREVIEW_MODES.DESKTOP);
      expect(screen.getByTestId('mobile-width')).toHaveTextContent(String(LAYOUT.MOBILE_WIDTH_DEFAULT));
      expect(screen.getByTestId('is-fullscreen')).toHaveTextContent('false');
      expect(screen.getByTestId('is-resizing')).toHaveTextContent('false');
    });

    it('accepts custom initial layout', () => {
      const customLayout = {
        splitSize: 30,
        viewMode: PREVIEW_MODES.MOBILE,
        mobileWidth: 400,
        isFullscreen: true
      };

      render(<TestComponent initialLayout={customLayout} />);

      expect(screen.getByTestId('split-size')).toHaveTextContent('30');
      expect(screen.getByTestId('view-mode')).toHaveTextContent(PREVIEW_MODES.MOBILE);
      expect(screen.getByTestId('mobile-width')).toHaveTextContent('400');
      expect(screen.getByTestId('is-fullscreen')).toHaveTextContent('true');
    });

    it('computes isMobileView correctly', () => {
      render(<TestComponent initialLayout={{ viewMode: PREVIEW_MODES.MOBILE }} />);

      expect(screen.getByTestId('is-mobile-view')).toHaveTextContent('true');
      expect(screen.getByTestId('is-desktop-view')).toHaveTextContent('false');
    });

    it('computes isDesktopView correctly', () => {
      render(<TestComponent initialLayout={{ viewMode: PREVIEW_MODES.DESKTOP }} />);

      expect(screen.getByTestId('is-mobile-view')).toHaveTextContent('false');
      expect(screen.getByTestId('is-desktop-view')).toHaveTextContent('true');
    });
  });

  describe('setSplitSize', () => {
    it('updates split size', () => {
      render(<TestComponent />);

      act(() => {
        fireEvent.click(screen.getByTestId('set-split'));
      });

      expect(screen.getByTestId('split-size')).toHaveTextContent('60');
    });

    it('rejects split size below 10', () => {
      let layoutState;
      render(<TestComponent onStateChange={(state) => { layoutState = state; }} />);

      act(() => {
        layoutState.setSplitSize(5);
      });

      expect(screen.getByTestId('split-size')).toHaveTextContent('50'); // Should remain unchanged
    });

    it('rejects split size above 90', () => {
      let layoutState;
      render(<TestComponent onStateChange={(state) => { layoutState = state; }} />);

      act(() => {
        layoutState.setSplitSize(95);
      });

      expect(screen.getByTestId('split-size')).toHaveTextContent('50'); // Should remain unchanged
    });

    it('updates splitSizes array when split size changes', () => {
      let layoutState;
      render(<TestComponent onStateChange={(state) => { layoutState = state; }} />);

      act(() => {
        layoutState.setSplitSize(70);
      });

      expect(layoutState.splitSizes).toEqual([70, 30]);
    });
  });

  describe('setViewMode', () => {
    it('changes view mode to mobile', () => {
      render(<TestComponent />);

      act(() => {
        fireEvent.click(screen.getByTestId('set-mobile'));
      });

      expect(screen.getByTestId('view-mode')).toHaveTextContent(PREVIEW_MODES.MOBILE);
    });

    it('ignores invalid view modes', () => {
      let layoutState;
      render(<TestComponent onStateChange={(state) => { layoutState = state; }} />);

      act(() => {
        layoutState.setViewMode('invalid-mode');
      });

      expect(screen.getByTestId('view-mode')).toHaveTextContent(PREVIEW_MODES.DESKTOP); // Should remain unchanged
    });

    it('accepts all valid preview modes', () => {
      let layoutState;
      render(<TestComponent onStateChange={(state) => { layoutState = state; }} />);

      Object.values(PREVIEW_MODES).forEach(mode => {
        act(() => {
          layoutState.setViewMode(mode);
        });
        expect(screen.getByTestId('view-mode')).toHaveTextContent(mode);
      });
    });
  });

  describe('toggleViewMode', () => {
    it('toggles from desktop to mobile', () => {
      render(<TestComponent initialLayout={{ viewMode: PREVIEW_MODES.DESKTOP }} />);

      act(() => {
        fireEvent.click(screen.getByTestId('toggle-view'));
      });

      expect(screen.getByTestId('view-mode')).toHaveTextContent(PREVIEW_MODES.MOBILE);
    });

    it('toggles from mobile to desktop', () => {
      render(<TestComponent initialLayout={{ viewMode: PREVIEW_MODES.MOBILE }} />);

      act(() => {
        fireEvent.click(screen.getByTestId('toggle-view'));
      });

      expect(screen.getByTestId('view-mode')).toHaveTextContent(PREVIEW_MODES.DESKTOP);
    });

    it('toggles multiple times correctly', () => {
      render(<TestComponent />);

      act(() => {
        fireEvent.click(screen.getByTestId('toggle-view'));
      });
      expect(screen.getByTestId('view-mode')).toHaveTextContent(PREVIEW_MODES.MOBILE);

      act(() => {
        fireEvent.click(screen.getByTestId('toggle-view'));
      });
      expect(screen.getByTestId('view-mode')).toHaveTextContent(PREVIEW_MODES.DESKTOP);
    });
  });

  describe('setMobileWidth', () => {
    it('updates mobile width', () => {
      render(<TestComponent />);

      act(() => {
        fireEvent.click(screen.getByTestId('set-width'));
      });

      expect(screen.getByTestId('mobile-width')).toHaveTextContent('400');
    });

    it('constrains width to minimum 320', () => {
      let layoutState;
      render(<TestComponent onStateChange={(state) => { layoutState = state; }} />);

      act(() => {
        layoutState.setMobileWidth(200);
      });

      expect(screen.getByTestId('mobile-width')).toHaveTextContent('320');
    });

    it('constrains width to maximum 768', () => {
      let layoutState;
      render(<TestComponent onStateChange={(state) => { layoutState = state; }} />);

      act(() => {
        layoutState.setMobileWidth(1000);
      });

      expect(screen.getByTestId('mobile-width')).toHaveTextContent('768');
    });

    it('accepts width within valid range', () => {
      let layoutState;
      render(<TestComponent onStateChange={(state) => { layoutState = state; }} />);

      const validWidths = [320, 375, 400, 500, 600, 768];
      validWidths.forEach(width => {
        act(() => {
          layoutState.setMobileWidth(width);
        });
        expect(screen.getByTestId('mobile-width')).toHaveTextContent(String(width));
      });
    });
  });

  describe('toggleFullscreen', () => {
    it('toggles fullscreen on', () => {
      render(<TestComponent />);

      act(() => {
        fireEvent.click(screen.getByTestId('toggle-fullscreen'));
      });

      expect(screen.getByTestId('is-fullscreen')).toHaveTextContent('true');
    });

    it('toggles fullscreen off', () => {
      render(<TestComponent initialLayout={{ isFullscreen: true }} />);

      act(() => {
        fireEvent.click(screen.getByTestId('toggle-fullscreen'));
      });

      expect(screen.getByTestId('is-fullscreen')).toHaveTextContent('false');
    });

    it('toggles multiple times', () => {
      render(<TestComponent />);

      act(() => {
        fireEvent.click(screen.getByTestId('toggle-fullscreen'));
      });
      expect(screen.getByTestId('is-fullscreen')).toHaveTextContent('true');

      act(() => {
        fireEvent.click(screen.getByTestId('toggle-fullscreen'));
      });
      expect(screen.getByTestId('is-fullscreen')).toHaveTextContent('false');
    });
  });

  describe('resetLayout', () => {
    it('resets layout to initial defaults', () => {
      // Note: resetLayout resets to the initial layout provided to the hook
      // If custom initial layout was provided, it resets to that
      const initialLayout = {
        splitSize: 70,
        viewMode: PREVIEW_MODES.MOBILE,
        mobileWidth: 600,
        isFullscreen: true
      };

      let layoutState;
      render(<TestComponent
        initialLayout={initialLayout}
        onStateChange={(state) => { layoutState = state; }}
      />);

      // Change some values
      act(() => {
        layoutState.setSplitSize(30);
        layoutState.setViewMode(PREVIEW_MODES.DESKTOP);
      });

      // Verify values changed
      expect(screen.getByTestId('split-size')).toHaveTextContent('30');
      expect(screen.getByTestId('view-mode')).toHaveTextContent(PREVIEW_MODES.DESKTOP);

      // Reset should go back to initial values
      act(() => {
        fireEvent.click(screen.getByTestId('reset'));
      });

      expect(screen.getByTestId('split-size')).toHaveTextContent('70');
      expect(screen.getByTestId('view-mode')).toHaveTextContent(PREVIEW_MODES.MOBILE);
      expect(screen.getByTestId('mobile-width')).toHaveTextContent('600');
      expect(screen.getByTestId('is-fullscreen')).toHaveTextContent('true');
    });
  });

  describe('setResizingState', () => {
    it('sets resizing to true', () => {
      render(<TestComponent />);

      act(() => {
        fireEvent.click(screen.getByTestId('start-resize'));
      });

      expect(screen.getByTestId('is-resizing')).toHaveTextContent('true');
    });

    it('sets resizing to false', () => {
      let layoutState;
      render(<TestComponent onStateChange={(state) => { layoutState = state; }} />);

      act(() => {
        layoutState.setResizingState(true);
      });
      expect(screen.getByTestId('is-resizing')).toHaveTextContent('true');

      act(() => {
        layoutState.setResizingState(false);
      });
      expect(screen.getByTestId('is-resizing')).toHaveTextContent('false');
    });
  });

  describe('updateSplitSizes', () => {
    it('updates split sizes array', () => {
      let layoutState;
      render(<TestComponent onStateChange={(state) => { layoutState = state; }} />);

      act(() => {
        fireEvent.click(screen.getByTestId('update-sizes'));
      });

      expect(layoutState.splitSizes).toEqual([40, 60]);
      expect(screen.getByTestId('split-size')).toHaveTextContent('40');
    });

    it('rejects sizes below minimum', () => {
      let layoutState;
      render(<TestComponent onStateChange={(state) => { layoutState = state; }} />);

      act(() => {
        layoutState.updateSplitSizes([10, 90]);
      });

      expect(screen.getByTestId('split-size')).toHaveTextContent('50'); // Should remain unchanged
    });

    it('rejects sizes above maximum', () => {
      let layoutState;
      render(<TestComponent onStateChange={(state) => { layoutState = state; }} />);

      act(() => {
        layoutState.updateSplitSizes([85, 15]);
      });

      expect(screen.getByTestId('split-size')).toHaveTextContent('50'); // Should remain unchanged
    });

    it('rejects sizes that do not add up to 100', () => {
      let layoutState;
      render(<TestComponent onStateChange={(state) => { layoutState = state; }} />);

      act(() => {
        layoutState.updateSplitSizes([50, 45]);
      });

      expect(screen.getByTestId('split-size')).toHaveTextContent('50'); // Should remain unchanged
    });
  });

  describe('handleResize', () => {
    it('calculates new sizes based on delta', () => {
      let layoutState;
      render(<TestComponent onStateChange={(state) => { layoutState = state; }} />);

      act(() => {
        layoutState.handleResize(100, 1000); // 10% increase
      });

      expect(layoutState.splitSizes[0]).toBeCloseTo(60, 0);
      expect(layoutState.splitSizes[1]).toBeCloseTo(40, 0);
    });

    it('respects minimum pane size constraints', () => {
      let layoutState;
      render(<TestComponent
        initialLayout={{ splitSizes: [25, 75], splitSize: 25 }}
        onStateChange={(state) => { layoutState = state; }}
      />);

      act(() => {
        layoutState.handleResize(-100, 1000); // Try to shrink below min
      });

      expect(layoutState.splitSizes[0]).toBeGreaterThanOrEqual(LAYOUT.MIN_PANE_SIZE);
    });

    it('ignores zero container size', () => {
      let layoutState;
      render(<TestComponent onStateChange={(state) => { layoutState = state; }} />);

      const initialSizes = [...layoutState.splitSizes];

      act(() => {
        layoutState.handleResize(100, 0);
      });

      expect(layoutState.splitSizes).toEqual(initialSizes); // Should remain unchanged
    });
  });

  describe('Computed Properties', () => {
    it('exposes layout constraints', () => {
      let layoutState;
      render(<TestComponent onStateChange={(state) => { layoutState = state; }} />);

      expect(layoutState.minPaneSize).toBe(LAYOUT.MIN_PANE_SIZE);
      expect(layoutState.maxPaneSize).toBe(LAYOUT.MAX_PANE_SIZE);
      expect(layoutState.gutterSize).toBe(LAYOUT.GUTTER_SIZE);
    });
  });

  describe('Keyboard Shortcuts', () => {
    it('handles F11 for fullscreen', () => {
      render(<TestComponent />);

      act(() => {
        const event = new KeyboardEvent('keydown', { key: 'F11' });
        document.dispatchEvent(event);
      });

      expect(screen.getByTestId('is-fullscreen')).toHaveTextContent('true');
    });

    it('handles Ctrl+P for view toggle', () => {
      render(<TestComponent />);

      act(() => {
        const event = new KeyboardEvent('keydown', { key: 'p', ctrlKey: true });
        document.dispatchEvent(event);
      });

      expect(screen.getByTestId('view-mode')).toHaveTextContent(PREVIEW_MODES.MOBILE);
    });

    it('handles Cmd+P for view toggle on Mac', () => {
      render(<TestComponent />);

      act(() => {
        const event = new KeyboardEvent('keydown', { key: 'p', metaKey: true });
        document.dispatchEvent(event);
      });

      expect(screen.getByTestId('view-mode')).toHaveTextContent(PREVIEW_MODES.MOBILE);
    });
  });

  describe('Responsive Behavior', () => {
    it('switches to mobile view on small screens', () => {
      // Mock window.innerWidth
      Object.defineProperty(window, 'innerWidth', {
        writable: true,
        configurable: true,
        value: 500 // Below MOBILE_BREAKPOINT
      });

      render(<TestComponent initialLayout={{ viewMode: PREVIEW_MODES.DESKTOP }} />);

      act(() => {
        window.dispatchEvent(new Event('resize'));
      });

      expect(screen.getByTestId('view-mode')).toHaveTextContent(PREVIEW_MODES.MOBILE);
    });

    it('does not auto-switch when already in mobile view', () => {
      Object.defineProperty(window, 'innerWidth', {
        writable: true,
        configurable: true,
        value: 500
      });

      render(<TestComponent initialLayout={{ viewMode: PREVIEW_MODES.MOBILE }} />);

      act(() => {
        window.dispatchEvent(new Event('resize'));
      });

      expect(screen.getByTestId('view-mode')).toHaveTextContent(PREVIEW_MODES.MOBILE);
    });
  });

  describe('Edge Cases', () => {
    it('handles boundary split sizes', () => {
      let layoutState;
      render(<TestComponent onStateChange={(state) => { layoutState = state; }} />);

      act(() => {
        layoutState.setSplitSize(10); // Minimum
      });
      expect(screen.getByTestId('split-size')).toHaveTextContent('10');

      act(() => {
        layoutState.setSplitSize(90); // Maximum
      });
      expect(screen.getByTestId('split-size')).toHaveTextContent('90');
    });

    it('handles exact constraint boundaries for mobile width', () => {
      let layoutState;
      render(<TestComponent onStateChange={(state) => { layoutState = state; }} />);

      act(() => {
        layoutState.setMobileWidth(320); // Min
      });
      expect(screen.getByTestId('mobile-width')).toHaveTextContent('320');

      act(() => {
        layoutState.setMobileWidth(768); // Max
      });
      expect(screen.getByTestId('mobile-width')).toHaveTextContent('768');
    });
  });
});

