// @ts-nocheck
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';

// Stub foundry global for conditional base class — must NOT be present at module load time
// so the fallback class is used. The module is imported after this comment block.
// We only stub `foundry` in specific tests that need runtime foundry calls (none here).

beforeEach(() => {
  vi.stubGlobal('Hooks', {
    on: vi.fn(() => 99),
    off: vi.fn(),
  });
  vi.stubGlobal('game', {
    user: {
      setFlag: vi.fn(),
      getFlag: vi.fn(() => null),
    },
  });
});

afterEach(() => {
  vi.unstubAllGlobals();
  vi.clearAllMocks();
});

import { DirectorsBoard } from '../../../../src/ui/gm/DirectorsBoard.js';

describe('DirectorsBoard', () => {
  let stateStore;
  let controller;
  let adapter;
  let scenePresetManager;
  let board;

  beforeEach(() => {
    stateStore = { getState: vi.fn(() => 'active') };
    controller = { action: vi.fn(), hasPendingOp: vi.fn(() => false), getRevision: vi.fn(() => 0) };
    adapter = {
      users: {
        get: vi.fn(() => ({ name: 'Alice', avatar: null })),
        all: vi.fn(() => [{ id: 'u1' }]),
      },
      scenes: {
        current: vi.fn(() => null),
      },
    };
    scenePresetManager = {
      list: vi.fn(() => []),
      save: vi.fn(),
      load: vi.fn(),
      _getSceneFlagData: vi.fn(() => null),
      _getAutoApplyConfig: vi.fn(() => ({ enabled: false, presetName: null, preDelay: 0 })),
    };
    board = new DirectorsBoard(stateStore, controller, adapter, scenePresetManager);
  });

  describe('constructor', () => {
    it('is side-effect-free: does not call Hooks.on', () => {
      expect(Hooks.on).not.toHaveBeenCalled();
    });

    it('sets _hookId to null initially', () => {
      expect(board._hookId).toBeNull();
    });

    it('stores stateStore, controller, adapter, scenePresetManager references', () => {
      expect(board._stateStore).toBe(stateStore);
      expect(board._controller).toBe(controller);
      expect(board._adapter).toBe(adapter);
      expect(board._scenePresetManager).toBe(scenePresetManager);
    });

    it('initializes _saveDialog and _loadDialog to null', () => {
      expect(board._saveDialog).toBeNull();
      expect(board._loadDialog).toBeNull();
    });

    it('has DEFAULT_OPTIONS with position', () => {
      expect(DirectorsBoard.DEFAULT_OPTIONS.position).toEqual({
        width: 420,
        height: 480,
        left: 20,
        top: 100,
      });
    });
  });

  describe('_loadPosition()', () => {
    // Helper to create a board with options for testing _loadPosition
    function createBoardWithOptions(options = {}) {
      return new DirectorsBoard(
        stateStore,
        controller,
        adapter,
        scenePresetManager,
        undefined,
        options
      );
    }

    it('loads position from user flag when saved state has open=true', () => {
      const savedState = {
        open: true,
        left: 100,
        top: 200,
        width: 500,
        height: 600,
      };
      game.user.getFlag.mockReturnValue(savedState);

      const boardWithOptions = createBoardWithOptions({ position: {} });
      boardWithOptions._loadPosition();

      expect(game.user.getFlag).toHaveBeenCalledWith(
        'scrying-pool',
        'directorsBoardState'
      );
      // Position should be merged into options.position (not replaced)
      expect(boardWithOptions.options.position).toEqual({
        left: 100,
        top: 200,
        width: 500,
        height: 600,
      });
    });

    it('uses defaults when saved state has no width/height', () => {
      const savedState = {
        open: true,
        left: 100,
        top: 200,
      };
      game.user.getFlag.mockReturnValue(savedState);

      const boardWithOptions = createBoardWithOptions({ position: {} });
      boardWithOptions._loadPosition();

      expect(boardWithOptions.options.position).toEqual({
        left: 100,
        top: 200,
        width: 420,
        height: 480,
      });
    });

    it('does not modify position when saved state is null', () => {
      game.user.getFlag.mockReturnValue(null);

      const boardWithOptions = createBoardWithOptions({ position: { width: 400, height: 300 } });
      const originalPosition = boardWithOptions.options.position;
      boardWithOptions._loadPosition();

      expect(boardWithOptions.options.position).toBe(originalPosition);
    });

    it('does not modify position when open is false', () => {
      game.user.getFlag.mockReturnValue({ open: false, left: 100, top: 200 });

      const boardWithOptions = createBoardWithOptions({ position: { width: 400, height: 300 } });
      const originalPosition = boardWithOptions.options.position;
      boardWithOptions._loadPosition();

      expect(boardWithOptions.options.position).toBe(originalPosition);
    });

    it('does not modify position when left is null', () => {
      game.user.getFlag.mockReturnValue({ open: true, left: null, top: 200 });

      const boardWithOptions = createBoardWithOptions({ position: { width: 400, height: 300 } });
      const originalPosition = boardWithOptions.options.position;
      boardWithOptions._loadPosition();

      expect(boardWithOptions.options.position).toBe(originalPosition);
    });

    it('does not throw when options is undefined', () => {
      game.user.getFlag.mockReturnValue({ open: true, left: 100, top: 200 });

      // Board created without options (fallback class doesn't set options)
      expect(() => board._loadPosition()).not.toThrow();
    });

    it('handles errors gracefully and logs to console', () => {
      game.user.getFlag.mockImplementation(() => {
        throw new Error('Flag read error');
      });

      const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
      const boardWithOptions = createBoardWithOptions({ position: { width: 400, height: 300 } });
      const originalPosition = boardWithOptions.options.position;

      boardWithOptions._loadPosition();

      expect(consoleSpy).toHaveBeenCalledWith(
        '[ScryingPool] Failed to load directors board position:',
        expect.any(Error)
      );
      expect(boardWithOptions.options.position).toBe(originalPosition);
    });
  });

  describe('init()', () => {
    it('registers scrying-pool:stateChanged hook', () => {
      board.init();
      expect(Hooks.on).toHaveBeenCalledWith('scrying-pool:stateChanged', expect.any(Function));
    });

    it('stores the returned hook id in _hookId', () => {
      board.init();
      expect(board._hookId).toBe(99);
    });
  });

  describe('teardown()', () => {
    it('calls Hooks.off with the stored hook id', () => {
      board.init();
      board.teardown();
      expect(Hooks.off).toHaveBeenCalledWith('scrying-pool:stateChanged', 99);
    });

    it('sets _hookId to null after teardown', () => {
      board.init();
      board.teardown();
      expect(board._hookId).toBeNull();
    });

    it('is a no-op when init was not called', () => {
      expect(() => board.teardown()).not.toThrow();
      expect(Hooks.off).not.toHaveBeenCalled();
    });
  });

  describe('_dispatchToggle()', () => {
    it('calls controller.action with positional args (active→hidden)', () => {
      stateStore.getState.mockReturnValue('active');
      board._dispatchToggle('u1');
      expect(controller.action).toHaveBeenCalledWith('board', 'u1', 'hidden', expect.any(String), expect.any(Number));
    });

    it('calls controller.action with targetState active (hidden→active)', () => {
      stateStore.getState.mockReturnValue('hidden');
      board._dispatchToggle('u2');
      expect(controller.action).toHaveBeenCalledWith('board', 'u2', 'active', expect.any(String), expect.any(Number));
    });

    it('does not dispatch if userId is falsy', () => {
      board._dispatchToggle(null);
      board._dispatchToggle(undefined);
      board._dispatchToggle('');
      expect(controller.action).not.toHaveBeenCalled();
    });

    it('does not dispatch if controller reports pending op', () => {
      controller.hasPendingOp.mockReturnValue(true);
      board._dispatchToggle('u1');
      expect(controller.action).not.toHaveBeenCalled();
    });

    it('defaults to active state when stateStore returns null', () => {
      stateStore.getState.mockReturnValue(null);
      board._dispatchToggle('u1');
      expect(controller.action).toHaveBeenCalledWith('board', 'u1', 'hidden', expect.any(String), expect.any(Number));
    });
  });

  describe('toggle()', () => {
    it('calls render({ force: true }) when not rendered', async () => {
      board._rendered = false;
      const renderSpy = vi.spyOn(board, 'render').mockResolvedValue(undefined);
      await board.toggle();
      expect(renderSpy).toHaveBeenCalledWith({ force: true });
    });

    it('calls close() when rendered', async () => {
      board._rendered = true;
      const closeSpy = vi.spyOn(board, 'close').mockResolvedValue(undefined);
      await board.toggle();
      expect(closeSpy).toHaveBeenCalled();
    });
  });

  describe('_onStateChanged()', () => {
    it('calls render({ force: true }) when board is rendered', () => {
      board._rendered = true;
      const renderSpy = vi.spyOn(board, 'render').mockResolvedValue(undefined);
      board._onStateChanged({ userId: 'u1', newState: 'hidden' });
      expect(renderSpy).toHaveBeenCalledWith({ force: true });
    });

    it('does not call render when board is not rendered', () => {
      board._rendered = false;
      const renderSpy = vi.spyOn(board, 'render').mockResolvedValue(undefined);
      board._onStateChanged({ userId: 'u1', newState: 'hidden' });
      expect(renderSpy).not.toHaveBeenCalled();
    });
  });

  describe('_prepareContext()', () => {
    it('returns board context with participants from adapter', async () => {
      const ctx = await board._prepareContext({});
      expect(ctx.participants).toHaveLength(1);
      expect(ctx.participants[0].userId).toBe('u1');
      expect(ctx.isEmpty).toBe(false);
    });

    it('returns isEmpty=true when adapter has no users', async () => {
      adapter.users.all.mockReturnValue([]);
      const ctx = await board._prepareContext({});
      expect(ctx.isEmpty).toBe(true);
    });

    it('returns hasUndo=false when _undoSnapshot is null', async () => {
      board._undoSnapshot = null;
      const ctx = await board._prepareContext({});
      expect(ctx.hasUndo).toBe(false);
    });

    it('returns hasUndo=true when _undoSnapshot is set', async () => {
      board._undoSnapshot = new Map([['u1', 'hidden']]);
      const ctx = await board._prepareContext({});
      expect(ctx.hasUndo).toBe(true);
    });

    it('returns hasRestore=false when _spotlightSnapshot is null', async () => {
      board._spotlightSnapshot = null;
      const ctx = await board._prepareContext({});
      expect(ctx.hasRestore).toBe(false);
    });

    it('returns hasRestore=true when _spotlightSnapshot is set', async () => {
      board._spotlightSnapshot = new Map([['u1', 'active']]);
      const ctx = await board._prepareContext({});
      expect(ctx.hasRestore).toBe(true);
    });
  });

  describe('DEFAULT_OPTIONS', () => {
    it('has correct id', () => {
      expect(DirectorsBoard.DEFAULT_OPTIONS.id).toBe('scrying-pool-directors-board');
    });

    it('has classes including scrying-pool and directors-board', () => {
      expect(DirectorsBoard.DEFAULT_OPTIONS.classes).toContain('scrying-pool');
      expect(DirectorsBoard.DEFAULT_OPTIONS.classes).toContain('directors-board');
    });
  });

  describe('PARTS', () => {
    it('has a board part with the correct template path', () => {
      expect(DirectorsBoard.PARTS.board.template).toContain('directors-board.hbs');
    });
  });

  describe('showAll()', () => {
    it('calls controller.action with active for each non-ghost user', () => {
      adapter.users.all.mockReturnValue([{ id: 'u1' }, { id: 'u2' }, { id: 'u3' }]);
      stateStore.getState.mockImplementation(id => id === 'u3' ? 'ghost' : 'hidden');
      board.showAll();
      expect(controller.action).toHaveBeenCalledTimes(2);
      expect(controller.action).toHaveBeenCalledWith('board', 'u1', 'active', expect.any(String), expect.any(Number));
      expect(controller.action).toHaveBeenCalledWith('board', 'u2', 'active', expect.any(String), expect.any(Number));
      expect(controller.action).not.toHaveBeenCalledWith('board', 'u3', expect.anything(), expect.anything(), expect.anything());
    });

    it('stores pre-action snapshot in _undoSnapshot (non-ghost users only)', () => {
      adapter.users.all.mockReturnValue([{ id: 'u1' }, { id: 'u2' }, { id: 'u3' }]);
      stateStore.getState.mockImplementation(id => {
        if (id === 'u1') return 'hidden';
        if (id === 'u2') return 'active';
        return 'ghost';
      });
      board.showAll();
      expect(board._undoSnapshot).toBeInstanceOf(Map);
      expect(board._undoSnapshot.get('u1')).toBe('hidden');
      expect(board._undoSnapshot.get('u2')).toBe('active');
      expect(board._undoSnapshot.has('u3')).toBe(false);
    });

    it('clears _spotlightSnapshot when called', () => {
      board._spotlightSnapshot = new Map([['u1', 'active']]);
      adapter.users.all.mockReturnValue([{ id: 'u1' }]);
      stateStore.getState.mockReturnValue('hidden');
      board.showAll();
      expect(board._spotlightSnapshot).toBeNull();
    });

    it('skips participants with pending ops', () => {
      adapter.users.all.mockReturnValue([{ id: 'u1' }, { id: 'u2' }]);
      stateStore.getState.mockReturnValue('hidden');
      controller.hasPendingOp.mockImplementation(id => id === 'u1');
      board.showAll();
      expect(controller.action).toHaveBeenCalledTimes(1);
      expect(controller.action).toHaveBeenCalledWith('board', 'u2', 'active', expect.any(String), expect.any(Number));
    });

    it('is a no-op when all users are ghost', () => {
      adapter.users.all.mockReturnValue([{ id: 'u1' }]);
      stateStore.getState.mockReturnValue('ghost');
      board.showAll();
      expect(controller.action).not.toHaveBeenCalled();
    });
  });

  describe('hideAll()', () => {
    it('calls controller.action with hidden for each non-ghost user', () => {
      adapter.users.all.mockReturnValue([{ id: 'u1' }, { id: 'u2' }]);
      stateStore.getState.mockReturnValue('active');
      board.hideAll();
      expect(controller.action).toHaveBeenCalledTimes(2);
      expect(controller.action).toHaveBeenCalledWith('board', 'u1', 'hidden', expect.any(String), expect.any(Number));
      expect(controller.action).toHaveBeenCalledWith('board', 'u2', 'hidden', expect.any(String), expect.any(Number));
    });

    it('stores pre-action snapshot in _undoSnapshot', () => {
      adapter.users.all.mockReturnValue([{ id: 'u1' }]);
      stateStore.getState.mockReturnValue('active');
      board.hideAll();
      expect(board._undoSnapshot).toBeInstanceOf(Map);
      expect(board._undoSnapshot.get('u1')).toBe('active');
    });

    it('clears _spotlightSnapshot when called', () => {
      board._spotlightSnapshot = new Map([['u1', 'active']]);
      adapter.users.all.mockReturnValue([{ id: 'u1' }]);
      stateStore.getState.mockReturnValue('active');
      board.hideAll();
      expect(board._spotlightSnapshot).toBeNull();
    });

    it('excludes ghost-state participants', () => {
      adapter.users.all.mockReturnValue([{ id: 'u1' }, { id: 'u2' }]);
      stateStore.getState.mockImplementation(id => id === 'u2' ? 'ghost' : 'active');
      board.hideAll();
      expect(controller.action).toHaveBeenCalledTimes(1);
      expect(controller.action).toHaveBeenCalledWith('board', 'u1', 'hidden', expect.any(String), expect.any(Number));
    });
  });

  describe('undo()', () => {
    it('restores participants to snapshot states', () => {
      board._undoSnapshot = new Map([['u1', 'hidden'], ['u2', 'active']]);
      stateStore.getState.mockReturnValue('active');
      board.undo();
      expect(controller.action).toHaveBeenCalledWith('board', 'u1', 'hidden', expect.any(String), expect.any(Number));
      expect(controller.action).toHaveBeenCalledWith('board', 'u2', 'active', expect.any(String), expect.any(Number));
    });

    it('clears _undoSnapshot after use (single-step only)', async () => {
      board._undoSnapshot = new Map([['u1', 'hidden']]);
      stateStore.getState.mockReturnValue('active');
      await board.undo();
      expect(board._undoSnapshot).toBeNull();
    });

    it('is a no-op when _undoSnapshot is null', () => {
      board._undoSnapshot = null;
      board.undo();
      expect(controller.action).not.toHaveBeenCalled();
    });

    it('second undo is unavailable after first (no-op)', async () => {
      board._undoSnapshot = new Map([['u1', 'hidden']]);
      stateStore.getState.mockReturnValue('active');
      await board.undo();
      board.undo();
      expect(controller.action).toHaveBeenCalledTimes(1);
    });

    it('skips ghost-state participants during undo', () => {
      board._undoSnapshot = new Map([['u1', 'active'], ['u2', 'hidden']]);
      stateStore.getState.mockImplementation(id => id === 'u2' ? 'ghost' : 'active');
      board.undo();
      expect(controller.action).toHaveBeenCalledTimes(1);
      expect(controller.action).toHaveBeenCalledWith('board', 'u1', 'active', expect.any(String), expect.any(Number));
    });

    it('skips participants with pending ops during undo', () => {
      board._undoSnapshot = new Map([['u1', 'hidden'], ['u2', 'hidden']]);
      stateStore.getState.mockReturnValue('active');
      controller.hasPendingOp.mockImplementation(id => id === 'u1');
      board.undo();
      expect(controller.action).toHaveBeenCalledTimes(1);
      expect(controller.action).toHaveBeenCalledWith('board', 'u2', 'hidden', expect.any(String), expect.any(Number));
    });
  });
});

describe('DirectorsBoard spotlight', () => {
  let stateStore, controller, adapter, board;

  beforeEach(() => {
    vi.stubGlobal('Hooks', { on: vi.fn(() => 1), off: vi.fn(), once: vi.fn() });
    vi.stubGlobal('game', { keybindings: { register: vi.fn() } });
    stateStore = {
      getState: vi.fn(),
      getAll: vi.fn(() => new Map()),
    };
    controller = {
      action: vi.fn(),
      hasPendingOp: vi.fn(() => false),
      getRevision: vi.fn(() => 0),
    };
    adapter = {
      users: { all: vi.fn(() => [{ id: 'u1' }, { id: 'u2' }, { id: 'u3' }]) },
      scenes: { current: vi.fn(() => null) },
      i18n: { localize: vi.fn((key) => key) },
    };
    board = new DirectorsBoard(stateStore, controller, adapter, null);
    board.rendered = false;
    board.render = vi.fn();
  });

  afterEach(() => vi.unstubAllGlobals());

  describe('spotlight(userId)', () => {
    it('sets focusedId active, all others hidden, captures snapshot, clears undo', () => {
      stateStore.getState.mockImplementation(id => ({ u1: 'hidden', u2: 'active', u3: 'active' }[id]));
      board._undoSnapshot = new Map([['u1', 'hidden']]);
      board.spotlight('u1');
      expect(controller.action).toHaveBeenCalledWith('board', 'u1', 'active', expect.any(String), expect.any(Number));
      expect(controller.action).toHaveBeenCalledWith('board', 'u2', 'hidden', expect.any(String), expect.any(Number));
      expect(controller.action).toHaveBeenCalledWith('board', 'u3', 'hidden', expect.any(String), expect.any(Number));
    });

    it('stores pre-spotlight snapshot in _spotlightSnapshot', () => {
      stateStore.getState.mockImplementation(id => id === 'u1' ? 'active' : 'hidden');
      board.spotlight('u1');
      expect(board._spotlightSnapshot).toBeInstanceOf(Map);
      expect(board._spotlightSnapshot.size).toBe(3);
    });

    it('clears _undoSnapshot when spotlight is called', () => {
      stateStore.getState.mockReturnValue('active');
      board._undoSnapshot = new Map([['u1', 'hidden']]);
      board.spotlight('u2');
      expect(board._undoSnapshot).toBeNull();
    });

    it('excludes ghost participants from spotlight', () => {
      stateStore.getState.mockImplementation(id => id === 'u3' ? 'ghost' : 'active');
      board.spotlight('u1');
      const calls = controller.action.mock.calls.map(c => c[1]);
      expect(calls).not.toContain('u3');
    });
  });

  describe('restoreSpotlight()', () => {
    it('restores participants to pre-spotlight snapshot states', () => {
      board._spotlightSnapshot = new Map([['u1', 'hidden'], ['u2', 'active'], ['u3', 'hidden']]);
      stateStore.getState.mockReturnValue('active');
      board.restoreSpotlight();
      expect(controller.action).toHaveBeenCalledWith('board', 'u1', 'hidden', expect.any(String), expect.any(Number));
      expect(controller.action).toHaveBeenCalledWith('board', 'u2', 'active', expect.any(String), expect.any(Number));
    });

    it('clears _spotlightSnapshot after restore', async () => {
      board._spotlightSnapshot = new Map([['u1', 'active']]);
      stateStore.getState.mockReturnValue('active');
      await board.restoreSpotlight();
      expect(board._spotlightSnapshot).toBeNull();
    });

    it('is a no-op when _spotlightSnapshot is null', () => {
      board._spotlightSnapshot = null;
      board.restoreSpotlight();
      expect(controller.action).not.toHaveBeenCalled();
    });

    it('skips ghost participants during restore', () => {
      board._spotlightSnapshot = new Map([['u1', 'active'], ['u2', 'hidden']]);
      stateStore.getState.mockImplementation(id => id === 'u2' ? 'ghost' : 'active');
      board.restoreSpotlight();
      expect(controller.action).toHaveBeenCalledTimes(1);
      expect(controller.action).toHaveBeenCalledWith('board', 'u1', 'active', expect.any(String), expect.any(Number));
    });
  });

  describe('spotlightFocused()', () => {
    it('is a no-op if no participant is focused', () => {
      board._focusedUserId = null;
      board.spotlightFocused();
      expect(controller.action).not.toHaveBeenCalled();
    });

    it('calls spotlight() with the currently focused userId', () => {
      stateStore.getState.mockReturnValue('active');
      board._focusedUserId = 'u2';
      const spy = vi.spyOn(board, 'spotlight');
      board.spotlightFocused();
      expect(spy).toHaveBeenCalledWith('u2');
    });
  });

  // --------------------------------------------------------------------------
  // Preset Save/Load Tests
  // --------------------------------------------------------------------------

  describe('_prepareContext() with presets', () => {
    it('should include presetCount in context', async () => {
      // Create a board with a scenePresetManager that has presets
      const presetManagerWithPresets = {
        list: vi.fn().mockReturnValue([
          { name: 'Preset 1' },
          { name: 'Preset 2' },
        ]),
        save: vi.fn(),
        load: vi.fn(),
      };
      const boardWithPresets = new DirectorsBoard(stateStore, controller, adapter, presetManagerWithPresets);
      
      const context = await boardWithPresets._prepareContext();
      
      expect(context.presetCount).toBe(2);
      expect(context.hasPresets).toBe(true);
    });

    it('should include hasPresets false when no presets', async () => {
      // Create a board with a scenePresetManager that has no presets
      const presetManagerNoPresets = {
        list: vi.fn().mockReturnValue([]),
        save: vi.fn(),
        load: vi.fn(),
      };
      const boardNoPresets = new DirectorsBoard(stateStore, controller, adapter, presetManagerNoPresets);
      
      const context = await boardNoPresets._prepareContext();
      
      expect(context.presetCount).toBe(0);
      expect(context.hasPresets).toBe(false);
    });

    it('should handle missing scenePresetManager gracefully', async () => {
      const boardWithoutManager = new DirectorsBoard(stateStore, controller, adapter, null);
      
      const context = await boardWithoutManager._prepareContext();
      
      expect(context.presetCount).toBe(0);
      expect(context.hasPresets).toBe(false);
    });
  });

  describe('_onSavePreset()', () => {
    it('should have _onSavePreset method defined', () => {
      expect(board._onSavePreset).toBeDefined();
      expect(typeof board._onSavePreset).toBe('function');
    });

    it('should have _onLoadPreset method defined', () => {
      expect(board._onLoadPreset).toBeDefined();
      expect(typeof board._onLoadPreset).toBe('function');
    });

    it('should have _closePresetDialogs method defined', () => {
      expect(board._closePresetDialogs).toBeDefined();
      expect(typeof board._closePresetDialogs).toBe('function');
    });
  });

  describe('click handler with preset actions', () => {
    it('should have click handler that processes save-preset action', () => {
      // The click handler is created in _onRender, so we need to set up the element first
      const mockElement = {
        querySelectorAll: vi.fn().mockReturnValue([]),
        querySelector: vi.fn(() => null),
        addEventListener: vi.fn(),
        removeEventListener: vi.fn(),
        prepend: vi.fn(),
        after: vi.fn(),
      };
      board.element = mockElement;
      board.rendered = true;
      
      board._onRender(mockElement);
      
      const clickHandler = board._clickHandler;
      expect(clickHandler).toBeDefined();
      
      // Verify the handler processes the action by checking it doesn't throw
      expect(typeof clickHandler).toBe('function');
    });

    it('should have _onSavePreset method', () => {
      expect(board._onSavePreset).toBeDefined();
      expect(typeof board._onSavePreset).toBe('function');
    });

    it('should have _onLoadPreset method', () => {
      expect(board._onLoadPreset).toBeDefined();
      expect(typeof board._onLoadPreset).toBe('function');
    });
  });

  describe('cleanup on close', () => {
    it('should call _closePresetDialogs on _onClose', async () => {
      // Spy on the method
      const closeSpy = vi.spyOn(board, '_closePresetDialogs');
      
      // Call _onClose
      await board._onClose({});
      
      // _closePresetDialogs should be called
      expect(closeSpy).toHaveBeenCalled();
      
      closeSpy.mockRestore();
    });

    it('should close save dialog on _closePresetDialogs', () => {
      // Use the board created in beforeEach
      const saveDialog = { close: vi.fn().mockResolvedValue({}) };
      board._saveDialog = saveDialog;
      board._loadDialog = null;
      
      board._closePresetDialogs();
      
      expect(saveDialog.close).toHaveBeenCalled();
      expect(board._saveDialog).toBeNull();
    });

    it('should close load dialog on _closePresetDialogs', () => {
      // Use the board created in beforeEach
      const loadDialog = { close: vi.fn().mockResolvedValue({}) };
      board._saveDialog = null;
      board._loadDialog = loadDialog;
      
      board._closePresetDialogs();
      
      expect(loadDialog.close).toHaveBeenCalled();
      expect(board._loadDialog).toBeNull();
    });
  });
});
