import { describe, it, expect, beforeEach, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import DiceRoller from '~/components/Gameplay/DiceRoller.vue'
import type { RollData } from '~/types'

describe('DiceRoller', () => {
  const createRollData = (overrides = {}): RollData => ({
    roll_id: 'test-roll-123',
    d6_one: 3,
    d6_two_a: 2,
    d6_two_b: 4,
    d6_two_total: 6,
    chaos_d20: 15,
    resolution_d20: 8,
    check_wild_pitch: false,
    check_passed_ball: false,
    timestamp: '2025-01-13T12:00:00Z',
    ...overrides,
  })

  beforeEach(() => {
    vi.clearAllTimers()
    vi.useFakeTimers()
  })

  // ============================================================================
  // Rendering Tests
  // ============================================================================

  describe('Rendering', () => {
    it('renders roll button when no pending roll', () => {
      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: true,
          pendingRoll: null,
        },
      })

      expect(wrapper.find('.roll-button').exists()).toBe(true)
      expect(wrapper.text()).toContain('Roll Dice')
      expect(wrapper.find('.dice-results').exists()).toBe(false)
    })

    it('renders dice results when pending roll exists', () => {
      const rollData = createRollData()
      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: false,
          pendingRoll: rollData,
        },
      })

      expect(wrapper.find('.roll-button').exists()).toBe(false)
      expect(wrapper.find('.dice-results').exists()).toBe(true)
      expect(wrapper.text()).toContain('Dice Results')
    })

    it('displays all four dice values correctly', () => {
      const rollData = createRollData({
        d6_one: 5,
        d6_two_total: 8,
        chaos_d20: 17,
        resolution_d20: 3,
      })

      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: false,
          pendingRoll: rollData,
        },
      })

      const diceValues = wrapper.findAll('.dice-value')
      expect(diceValues).toHaveLength(4)
      expect(diceValues[0].text()).toBe('5')
      expect(diceValues[1].text()).toBe('8')
      expect(diceValues[2].text()).toBe('17')
      expect(diceValues[3].text()).toBe('3')
    })

    it('displays d6_two component dice values', () => {
      const rollData = createRollData({
        d6_two_a: 3,
        d6_two_b: 5,
        d6_two_total: 8,
      })

      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: false,
          pendingRoll: rollData,
        },
      })

      expect(wrapper.text()).toContain('(3 + 5)')
    })
  })

  // ============================================================================
  // Button State Tests
  // ============================================================================

  describe('Button States', () => {
    it('enables button when canRoll is true', () => {
      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: true,
          pendingRoll: null,
        },
      })

      const button = wrapper.find('.roll-button')
      expect(button.classes()).toContain('roll-button-enabled')
      expect(button.classes()).not.toContain('roll-button-disabled')
      expect(button.attributes('disabled')).toBeUndefined()
    })

    it('disables button when canRoll is false', () => {
      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: false,
          pendingRoll: null,
        },
      })

      const button = wrapper.find('.roll-button')
      expect(button.classes()).toContain('roll-button-disabled')
      expect(button.classes()).not.toContain('roll-button-enabled')
      expect(button.attributes('disabled')).toBeDefined()
    })

    it('shows loading state when rolling', async () => {
      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: true,
          pendingRoll: null,
        },
      })

      await wrapper.find('.roll-button').trigger('click')

      expect(wrapper.text()).toContain('Rolling...')
      expect(wrapper.find('.animate-spin').exists()).toBe(true)
    })

    it('disables button during rolling animation', async () => {
      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: true,
          pendingRoll: null,
        },
      })

      await wrapper.find('.roll-button').trigger('click')
      const button = wrapper.find('.roll-button')

      expect(button.attributes('disabled')).toBeDefined()
    })

    it('resets rolling state after timeout', async () => {
      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: true,
          pendingRoll: null,
        },
      })

      await wrapper.find('.roll-button').trigger('click')
      expect(wrapper.text()).toContain('Rolling...')

      vi.advanceTimersByTime(2000)
      await wrapper.vm.$nextTick()

      expect(wrapper.text()).not.toContain('Rolling...')
    })
  })

  // ============================================================================
  // Event Emission Tests
  // ============================================================================

  describe('Event Emission', () => {
    it('emits roll event when button clicked', async () => {
      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: true,
          pendingRoll: null,
        },
      })

      await wrapper.find('.roll-button').trigger('click')

      expect(wrapper.emitted('roll')).toBeTruthy()
      expect(wrapper.emitted('roll')).toHaveLength(1)
    })

    it('does not emit roll when canRoll is false', async () => {
      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: false,
          pendingRoll: null,
        },
      })

      await wrapper.find('.roll-button').trigger('click')

      expect(wrapper.emitted('roll')).toBeFalsy()
    })

    it('does not emit roll during rolling animation', async () => {
      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: true,
          pendingRoll: null,
        },
      })

      await wrapper.find('.roll-button').trigger('click')
      await wrapper.find('.roll-button').trigger('click')

      expect(wrapper.emitted('roll')).toHaveLength(1)
    })
  })

  // ============================================================================
  // Special Events Tests
  // ============================================================================

  describe('Special Events', () => {
    it('shows wild pitch indicator when check_wild_pitch is true', () => {
      const rollData = createRollData({ check_wild_pitch: true })
      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: false,
          pendingRoll: rollData,
        },
      })

      expect(wrapper.find('.wild-pitch').exists()).toBe(true)
      expect(wrapper.text()).toContain('Wild Pitch Check')
    })

    it('shows passed ball indicator when check_passed_ball is true', () => {
      const rollData = createRollData({ check_passed_ball: true })
      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: false,
          pendingRoll: rollData,
        },
      })

      expect(wrapper.find('.passed-ball').exists()).toBe(true)
      expect(wrapper.text()).toContain('Passed Ball Check')
    })

    it('shows both indicators when both checks are true', () => {
      const rollData = createRollData({
        check_wild_pitch: true,
        check_passed_ball: true,
      })

      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: false,
          pendingRoll: rollData,
        },
      })

      expect(wrapper.find('.wild-pitch').exists()).toBe(true)
      expect(wrapper.find('.passed-ball').exists()).toBe(true)
    })

    it('hides special events section when no checks are true', () => {
      const rollData = createRollData({
        check_wild_pitch: false,
        check_passed_ball: false,
      })

      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: false,
          pendingRoll: rollData,
        },
      })

      expect(wrapper.find('.special-events').exists()).toBe(false)
    })
  })

  // ============================================================================
  // Card Instructions Tests
  // ============================================================================

  describe('Card Instructions', () => {
    it('shows card reading instructions when roll exists', () => {
      const rollData = createRollData()
      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: false,
          pendingRoll: rollData,
        },
      })

      expect(wrapper.find('.card-instructions').exists()).toBe(true)
      expect(wrapper.text()).toContain('Use these dice results')
    })

    it('does not show instructions when no roll', () => {
      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: true,
          pendingRoll: null,
        },
      })

      expect(wrapper.find('.card-instructions').exists()).toBe(false)
    })
  })

  // ============================================================================
  // Timestamp Formatting Tests
  // ============================================================================

  describe('Timestamp Formatting', () => {
    it('formats timestamp correctly', () => {
      const rollData = createRollData({
        timestamp: '2025-01-13T14:30:45Z',
      })

      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: false,
          pendingRoll: rollData,
        },
      })

      const timestampText = wrapper.find('.dice-header .text-sm').text()
      expect(timestampText).toMatch(/\d{1,2}:\d{2}:\d{2}/)
    })
  })

  // ============================================================================
  // Dice Type Styling Tests
  // ============================================================================

  describe('Dice Type Styling', () => {
    it('applies d6 styling to d6 dice', () => {
      const rollData = createRollData()
      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: false,
          pendingRoll: rollData,
        },
      })

      const diceItems = wrapper.findAll('.dice-item')
      expect(diceItems[0].classes()).toContain('dice-d6') // d6 one
      expect(diceItems[1].classes()).toContain('dice-d6') // d6 two
    })

    it('applies d20 styling to d20 dice', () => {
      const rollData = createRollData()
      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: false,
          pendingRoll: rollData,
        },
      })

      const diceItems = wrapper.findAll('.dice-item')
      expect(diceItems[2].classes()).toContain('dice-d20') // chaos d20
      expect(diceItems[3].classes()).toContain('dice-d20') // resolution d20
    })

    it('applies large value class to d20 dice', () => {
      const rollData = createRollData()
      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: false,
          pendingRoll: rollData,
        },
      })

      const diceValues = wrapper.findAll('.dice-value')
      expect(diceValues[2].classes()).toContain('dice-value-large')
      expect(diceValues[3].classes()).toContain('dice-value-large')
    })
  })

  // ============================================================================
  // Edge Cases
  // ============================================================================

  describe('Edge Cases', () => {
    it('handles maximum dice values', () => {
      const rollData = createRollData({
        d6_one: 6,
        d6_two_a: 6,
        d6_two_b: 6,
        d6_two_total: 12,
        chaos_d20: 20,
        resolution_d20: 20,
      })

      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: false,
          pendingRoll: rollData,
        },
      })

      expect(wrapper.text()).toContain('6')
      expect(wrapper.text()).toContain('12')
      expect(wrapper.text()).toContain('20')
    })

    it('handles minimum dice values', () => {
      const rollData = createRollData({
        d6_one: 1,
        d6_two_a: 1,
        d6_two_b: 1,
        d6_two_total: 2,
        chaos_d20: 1,
        resolution_d20: 1,
      })

      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: false,
          pendingRoll: rollData,
        },
      })

      expect(wrapper.text()).toContain('1')
      expect(wrapper.text()).toContain('2')
    })

    it('transitions from no roll to roll result', async () => {
      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: true,
          pendingRoll: null,
        },
      })

      expect(wrapper.find('.roll-button').exists()).toBe(true)
      expect(wrapper.find('.dice-results').exists()).toBe(false)

      await wrapper.setProps({ pendingRoll: createRollData(), canRoll: false })

      expect(wrapper.find('.roll-button').exists()).toBe(false)
      expect(wrapper.find('.dice-results').exists()).toBe(true)
    })

    it('clears roll result when pendingRoll set to null', async () => {
      const wrapper = mount(DiceRoller, {
        props: {
          canRoll: false,
          pendingRoll: createRollData(),
        },
      })

      expect(wrapper.find('.dice-results').exists()).toBe(true)

      await wrapper.setProps({ pendingRoll: null, canRoll: true })

      expect(wrapper.find('.dice-results').exists()).toBe(false)
      expect(wrapper.find('.roll-button').exists()).toBe(true)
    })
  })
})
