import { vi } from 'vitest'

import { dialogView, fieldMode, stageFocusType } from '../enums.js'
import { testItem, testRecipe, testState } from '../test-utils/index.js'

import uiEventHandlers from './ui-events.js'

// Mock external dependencies
vi.mock('file-saver', () => ({
  saveAs: vi.fn(),
}))

vi.mock('global/window.js', () => ({
  default: {
    location: {
      origin: 'http://localhost',
      pathname: '/',
      search: '',
      hash: '',
    },
    history: {
      replaceState: vi.fn(),
    },
  },
}))

vi.mock('../common/services/randomNumber.js', () => ({
  randomNumberService: {
    seedRandomNumber: vi.fn(),
    unseedRandomNumber: vi.fn(),
  },
}))

describe('UI Event Handlers', () => {
  let mockContext

  beforeEach(() => {
    // Create a mock context that simulates the Farmhand component
    mockContext = {
      state: {
        selectedItemId: '',
        fieldMode: fieldMode.OBSERVE,
        stageFocus: stageFocusType.HOME,
        hoveredPlotRangeSize: 0,
        isMenuOpen: false,
        money: 500,
        room: 'test-room',
        isAwaitingCowTradeRequest: false,
        currentDialogView: dialogView.NONE,
        showHomeScreen: true,
      },
      setState: vi.fn(),
      purchaseItem: vi.fn(),
      makeRecipe: vi.fn(),
      makeFermentationRecipe: vi.fn(),
      makeWine: vi.fn(),
      sellKeg: vi.fn(),
      removeKegFromCellar: vi.fn(),
      upgradeTool: vi.fn(),
      purchaseCow: vi.fn(),
      sellCow: vi.fn(),
      tradeForPeerCow: vi.fn(),
      changeCowAutomaticHugState: vi.fn(),
      changeCowBreedingPenResident: vi.fn(),
      hugCow: vi.fn(),
      offerCow: vi.fn(),
      withdrawCow: vi.fn(),
      changeCowName: vi.fn(),
      sellItem: vi.fn(),
      forRange: vi.fn(),
      setSprinkler: vi.fn(),
      setScarecrow: vi.fn(),
      incrementDay: vi.fn(),
      clearPersistedData: vi.fn(),
      waterAllPlots: vi.fn(),
      purchaseField: vi.fn(),
      purchaseForest: vi.fn(),
      purchaseCombine: vi.fn(),
      purchaseComposter: vi.fn(),
      purchaseSmelter: vi.fn(),
      purchaseCowPen: vi.fn(),
      purchaseCellar: vi.fn(),
      purchaseStorageExpansion: vi.fn(),
      focusNextView: vi.fn(),
      focusPreviousView: vi.fn(),
      selectCow: vi.fn(),
      openDialogView: vi.fn(),
      closeDialogView: vi.fn(),
      adjustLoan: vi.fn(),
      persistState: vi.fn(),
      showNotification: vi.fn(),
      createInitialState: vi.fn(() => ({})),
    }

    // Mock document.activeElement
    Object.defineProperty(document, 'activeElement', {
      value: { blur: vi.fn() },
      writable: true,
    })

    vi.clearAllMocks()
  })

  describe('handleFieldModeSelect', () => {
    test('switches to selected field mode and clears item selection', () => {
      const handler = uiEventHandlers.handleFieldModeSelect.bind(mockContext)

      handler(fieldMode.WATER)

      expect(mockContext.setState).toHaveBeenCalledWith(expect.any(Function))

      // Test the state updater function
      const stateUpdater = mockContext.setState.mock.calls[0][0]
      const newState = stateUpdater({ selectedItemId: 'test-item' })

      expect(newState).toEqual({
        selectedItemId: '',
        fieldMode: fieldMode.WATER,
      })
    })

    test('keeps selected item when switching to plant mode', () => {
      const handler = uiEventHandlers.handleFieldModeSelect.bind(mockContext)

      handler(fieldMode.PLANT)

      const stateUpdater = mockContext.setState.mock.calls[0][0]
      const newState = stateUpdater({ selectedItemId: 'crop-seed' })

      expect(newState).toEqual({
        selectedItemId: 'crop-seed',
        fieldMode: fieldMode.PLANT,
      })
    })
  })

  describe('handleViewChangeButtonClick', () => {
    test('navigates to selected view', () => {
      const handler = uiEventHandlers.handleViewChangeButtonClick.bind(
        mockContext
      )

      handler(stageFocusType.SHOP)

      expect(mockContext.setState).toHaveBeenCalledWith({
        stageFocus: stageFocusType.SHOP,
      })
    })
  })

  describe('handleItemSelectClick', () => {
    test('selects item and enables its field mode', () => {
      const handler = uiEventHandlers.handleItemSelectClick.bind(mockContext)
      const mockItem = testItem({
        id: 'test-item',
        enablesFieldMode: fieldMode.PLANT,
      })

      handler((mockItem as unknown) as Parameters<typeof handler>[0])

      expect(mockContext.setState).toHaveBeenCalledWith({
        fieldMode: fieldMode.PLANT,
        selectedItemId: 'test-item',
      })
    })
  })

  describe('handleMenuToggle', () => {
    test('opens menu when closed', () => {
      const handler = uiEventHandlers.handleMenuToggle.bind(mockContext)

      handler()

      expect(mockContext.setState).toHaveBeenCalledWith(expect.any(Function))

      // Test the state updater function
      const stateUpdater = mockContext.setState.mock.calls[0][0]
      const newState = stateUpdater({ isMenuOpen: false })

      expect(newState).toEqual({ isMenuOpen: true })
    })

    test('opens menu when explicitly requested', () => {
      const handler = uiEventHandlers.handleMenuToggle.bind(mockContext)

      handler((true as unknown) as Parameters<typeof handler>[0])

      const stateUpdater = mockContext.setState.mock.calls[0][0]
      const newState = stateUpdater({ isMenuOpen: false })

      expect(newState).toEqual({ isMenuOpen: true })
    })
  })

  describe('handleClickEndDayButton', () => {
    test('advances to next day', () => {
      const handler = uiEventHandlers.handleClickEndDayButton.bind(mockContext)

      handler()

      expect(mockContext.incrementDay).toHaveBeenCalled()
      expect(mockContext.incrementDay).toHaveBeenCalled()
      // Note: blur() is called on activeElement but is hard to test without DOM setup
    })
  })

  describe('handleItemPurchaseClick', () => {
    test('buys one item when quantity not specified', () => {
      const handler = uiEventHandlers.handleItemPurchaseClick.bind(mockContext)
      const mockItem = testItem({ id: 'test-item' })

      handler(mockItem as farmhand.item)

      expect(mockContext.purchaseItem).toHaveBeenCalledWith(mockItem, 1)
    })

    test('buys multiple items when quantity specified', () => {
      const handler = uiEventHandlers.handleItemPurchaseClick.bind(mockContext)
      const mockItem = testItem({ id: 'test-item' })

      handler(mockItem as farmhand.item, 5)

      expect(mockContext.purchaseItem).toHaveBeenCalledWith(mockItem, 5)
    })
  })

  describe('handleMakeRecipeClick', () => {
    test('crafts one recipe when quantity not specified', () => {
      const handler = uiEventHandlers.handleMakeRecipeClick.bind(mockContext)
      const mockRecipe = testRecipe({ id: 'test-recipe' })

      handler(mockRecipe as farmhand.recipe)

      expect(mockContext.makeRecipe).toHaveBeenCalledWith(mockRecipe, 1)
    })

    test('crafts multiple recipes when quantity specified', () => {
      const handler = uiEventHandlers.handleMakeRecipeClick.bind(mockContext)
      const mockRecipe = testRecipe({ id: 'test-recipe' })

      handler(mockRecipe as farmhand.recipe, 3)

      expect(mockContext.makeRecipe).toHaveBeenCalledWith(mockRecipe, 3)
    })
  })

  describe('handleCowSelect', () => {
    test('chooses cow for interaction', () => {
      const handler = uiEventHandlers.handleCowSelect.bind(mockContext)
      const mockCow = testState().cowForSale

      handler(mockCow)

      expect(mockContext.selectCow).toHaveBeenCalledWith(mockCow)
    })
  })

  describe('handleCowClick', () => {
    test('selects cow and gives it affection', () => {
      const handler = uiEventHandlers.handleCowClick.bind(mockContext)
      const mockCow = testState().cowForSale

      handler(mockCow)

      expect(mockContext.selectCow).toHaveBeenCalledWith(mockCow)
      expect(mockContext.hugCow).toHaveBeenCalledWith('test-cow')
    })
  })

  describe('handleClickDialogViewButton', () => {
    test('displays requested dialog screen', () => {
      const handler = uiEventHandlers.handleClickDialogViewButton.bind(
        mockContext
      )

      handler(dialogView.FARMERS_LOG)

      expect(mockContext.openDialogView).toHaveBeenCalledWith(
        dialogView.FARMERS_LOG
      )
    })
  })

  describe('handleCloseDialogView', () => {
    test('dismisses dialog when not waiting for trade response', () => {
      const handler = uiEventHandlers.handleCloseDialogView.bind(mockContext)

      handler()

      expect(mockContext.closeDialogView).toHaveBeenCalled()
    })

    test('keeps dialog open when waiting for trade response', () => {
      mockContext.state.isAwaitingCowTradeRequest = true
      const handler = uiEventHandlers.handleCloseDialogView.bind(mockContext)

      handler()

      expect(mockContext.closeDialogView).not.toHaveBeenCalled()
    })
  })

  describe('handleDialogViewExited', () => {
    test('clears dialog state after closing', () => {
      const handler = uiEventHandlers.handleDialogViewExited.bind(mockContext)

      handler()

      expect(mockContext.setState).toHaveBeenCalledWith({
        currentDialogView: dialogView.NONE,
      })
    })
  })

  describe('handleFieldActionRangeChange', () => {
    test('adjusts tool range preview size', () => {
      const handler = uiEventHandlers.handleFieldActionRangeChange.bind(
        mockContext
      )

      handler(3)

      expect(mockContext.setState).toHaveBeenCalledWith(expect.any(Function))

      const stateUpdater = mockContext.setState.mock.calls[0][0]
      const newState = stateUpdater({})

      expect(newState).toEqual({ hoveredPlotRangeSize: 3 })
    })
  })

  describe('handleAddMoneyClick', () => {
    test('increases player money by specified amount', () => {
      const handler = uiEventHandlers.handleAddMoneyClick.bind(mockContext)

      handler(1000)

      expect(mockContext.setState).toHaveBeenCalledWith(expect.any(Function))
    })
  })

  describe('handleOnlineToggleChange', () => {
    test('enters multiplayer mode and joins room', () => {
      const handler = uiEventHandlers.handleOnlineToggleChange.bind(mockContext)

      handler(true)

      expect(mockContext.setState).toHaveBeenCalledWith(expect.any(Function))

      const stateUpdater = mockContext.setState.mock.calls[0][0]
      const newState = stateUpdater({ room: 'test-room' })

      expect(newState).toEqual({
        redirect: '/online/test-room',
      })
    })

    test('exits multiplayer mode and returns to single player', () => {
      const handler = uiEventHandlers.handleOnlineToggleChange.bind(mockContext)

      handler(false)

      expect(mockContext.showNotification).toHaveBeenCalled()
      expect(mockContext.setState).toHaveBeenCalledWith(expect.any(Function))

      const stateUpdater = mockContext.setState.mock.calls[0][0]
      const newState = stateUpdater({ room: 'test-room' })

      expect(newState).toEqual({
        redirect: '/',
        cowIdOfferedForTrade: '',
      })
    })
  })

  describe('handleShowHomeScreenChange', () => {
    test('enables home screen display', () => {
      const handler = uiEventHandlers.handleShowHomeScreenChange.bind(
        mockContext
      )

      handler(null, true)

      expect(mockContext.setState).toHaveBeenCalledWith({
        showHomeScreen: true,
      })
    })

    test('navigates away from home when disabling home screen while viewing it', () => {
      mockContext.state.stageFocus = stageFocusType.HOME
      const handler = uiEventHandlers.handleShowHomeScreenChange.bind(
        mockContext
      )

      handler(null, false)

      expect(mockContext.focusNextView).toHaveBeenCalled()
      expect(mockContext.setState).toHaveBeenCalledWith({
        showHomeScreen: false,
      })
    })
  })

  describe('handleRNGSeedChange', () => {
    test('applies custom random seed for reproducible gameplay', () => {
      const handler = uiEventHandlers.handleRNGSeedChange.bind(mockContext)

      handler('test-seed')

      expect(mockContext.showNotification).toHaveBeenCalledWith(
        'Random seed set to "test-seed"',
        'success'
      )
    })

    test('removes custom seed to restore normal randomness', () => {
      const handler = uiEventHandlers.handleRNGSeedChange.bind(mockContext)

      handler('')

      expect(mockContext.showNotification).toHaveBeenCalledWith(
        'Random seed reset',
        'info'
      )
    })
  })

  describe('navigation handlers', () => {
    test('moves to next view', () => {
      const handler = uiEventHandlers.handleClickNextMenuButton.bind(
        mockContext
      )

      handler()

      expect(mockContext.focusNextView).toHaveBeenCalled()
    })

    test('moves to previous view', () => {
      const handler = uiEventHandlers.handleClickPreviousMenuButton.bind(
        mockContext
      )

      handler()

      expect(mockContext.focusPreviousView).toHaveBeenCalled()
    })
  })

  describe('purchase handlers', () => {
    test('expands farm with new field', () => {
      const handler = uiEventHandlers.handleFieldPurchase.bind(mockContext)

      handler(2)

      expect(mockContext.purchaseField).toHaveBeenCalledWith(2)
    })

    test('adds cow pen to farm', () => {
      const handler = uiEventHandlers.handleCowPenPurchase.bind(mockContext)

      handler(1)

      expect(mockContext.purchaseCowPen).toHaveBeenCalledWith(1)
    })

    test('increases inventory capacity', () => {
      const handler = uiEventHandlers.handleStorageExpansionPurchase.bind(
        mockContext
      )

      handler()

      expect(mockContext.purchaseStorageExpansion).toHaveBeenCalled()
    })
  })

  describe('loan handlers', () => {
    test('reduces debt by payment amount', () => {
      const handler = uiEventHandlers.handleClickLoanPaydownButton.bind(
        mockContext
      )

      handler(500)

      expect(mockContext.adjustLoan).toHaveBeenCalledWith(-500)
    })

    test('borrows money increasing debt', () => {
      const handler = uiEventHandlers.handleClickTakeOutLoanButton.bind(
        mockContext
      )

      handler(1000)

      expect(mockContext.adjustLoan).toHaveBeenCalledWith(1000)
    })
  })

  describe('utility handlers', () => {
    test('resets all saved game data', () => {
      const handler = uiEventHandlers.handleClearPersistedDataClick.bind(
        mockContext
      )

      handler()

      expect(mockContext.clearPersistedData).toHaveBeenCalled()
    })

    test('waters entire field at once', () => {
      const handler = uiEventHandlers.handleWaterAllPlotsClick.bind(mockContext)

      handler()

      expect(mockContext.waterAllPlots).toHaveBeenCalledWith(mockContext.state)
    })

    test('shows or hides chat window', () => {
      const handler = uiEventHandlers.handleChatRoomOpenStateChange.bind(
        mockContext
      )

      handler(true)

      expect(mockContext.setState).toHaveBeenCalledWith({ isChatOpen: true })
    })
  })
})
