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

describe('PlayResult', () => {
  const createPlayResult = (overrides = {}): PlayResultType => ({
    play_number: 1,
    outcome: 'STRIKEOUT',
    description: 'Mike Trout strikes out swinging',
    outs_recorded: 1,
    runs_scored: 0,
    runners_advanced: [],
    batter_result: null,
    new_state: {},
    is_hit: false,
    is_out: true,
    is_walk: false,
    is_strikeout: true,
    ...overrides,
  })

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

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

  describe('Rendering', () => {
    it('renders when result is provided', () => {
      const result = createPlayResult()
      const wrapper = mount(PlayResult, {
        props: { result },
      })

      expect(wrapper.find('.play-result').exists()).toBe(true)
      expect(wrapper.text()).toContain('Play #1')
    })

    it('does not render when result is null', () => {
      const wrapper = mount(PlayResult, {
        props: { result: null },
      })

      expect(wrapper.find('.play-result').exists()).toBe(false)
    })

    it('displays play description', () => {
      const result = createPlayResult({
        description: 'Custom play description',
      })

      const wrapper = mount(PlayResult, {
        props: { result },
      })

      expect(wrapper.text()).toContain('Custom play description')
    })

    it('displays play number correctly', () => {
      const result = createPlayResult({ play_number: 42 })
      const wrapper = mount(PlayResult, {
        props: { result },
      })

      expect(wrapper.text()).toContain('Play #42')
    })
  })

  // ============================================================================
  // Result Type Styling Tests
  // ============================================================================

  describe('Result Type Styling', () => {
    it('applies runs styling when runs scored', () => {
      const result = createPlayResult({
        runs_scored: 2,
        is_out: false,
      })

      const wrapper = mount(PlayResult, {
        props: { result },
      })

      expect(wrapper.find('.result-runs').exists()).toBe(true)
    })

    it('applies hit styling when is_hit is true', () => {
      const result = createPlayResult({
        is_hit: true,
        is_out: false,
        runs_scored: 0,
      })

      const wrapper = mount(PlayResult, {
        props: { result },
      })

      expect(wrapper.find('.result-hit').exists()).toBe(true)
    })

    it('applies out styling when is_out is true', () => {
      const result = createPlayResult({
        is_out: true,
        is_hit: false,
        runs_scored: 0,
      })

      const wrapper = mount(PlayResult, {
        props: { result },
      })

      expect(wrapper.find('.result-out').exists()).toBe(true)
    })

    it('applies default styling when no special type', () => {
      const result = createPlayResult({
        is_out: false,
        is_hit: false,
        runs_scored: 0,
      })

      const wrapper = mount(PlayResult, {
        props: { result },
      })

      expect(wrapper.find('.result-default').exists()).toBe(true)
    })

    it('prioritizes runs over hit styling', () => {
      const result = createPlayResult({
        runs_scored: 1,
        is_hit: true,
        is_out: false,
      })

      const wrapper = mount(PlayResult, {
        props: { result },
      })

      expect(wrapper.find('.result-runs').exists()).toBe(true)
      expect(wrapper.find('.result-hit').exists()).toBe(false)
    })
  })

  // ============================================================================
  // Stats Display Tests
  // ============================================================================

  describe('Stats Display', () => {
    it('displays outs recorded', () => {
      const result = createPlayResult({ outs_recorded: 2 })
      const wrapper = mount(PlayResult, {
        props: { result },
      })

      expect(wrapper.find('.stat-outs').exists()).toBe(true)
      expect(wrapper.find('.stat-outs').text()).toContain('2')
      expect(wrapper.find('.stat-outs').text()).toContain('Outs')
    })

    it('uses singular "Out" for single out', () => {
      const result = createPlayResult({ outs_recorded: 1 })
      const wrapper = mount(PlayResult, {
        props: { result },
      })

      expect(wrapper.find('.stat-outs').text()).toContain('Out')
      expect(wrapper.find('.stat-outs').text()).not.toContain('Outs')
    })

    it('displays runs scored', () => {
      const result = createPlayResult({
        runs_scored: 3,
        is_out: false,
      })

      const wrapper = mount(PlayResult, {
        props: { result },
      })

      expect(wrapper.find('.stat-runs').exists()).toBe(true)
      expect(wrapper.find('.stat-runs').text()).toContain('3')
      expect(wrapper.find('.stat-runs').text()).toContain('Runs')
    })

    it('uses singular "Run" for single run', () => {
      const result = createPlayResult({
        runs_scored: 1,
        is_out: false,
      })

      const wrapper = mount(PlayResult, {
        props: { result },
      })

      expect(wrapper.find('.stat-runs').text()).toContain('Run')
      expect(wrapper.find('.stat-runs').text()).not.toContain('Runs')
    })

    it('displays hit indicator when is_hit true', () => {
      const result = createPlayResult({
        is_hit: true,
        is_out: false,
      })

      const wrapper = mount(PlayResult, {
        props: { result },
      })

      expect(wrapper.find('.stat-hit').exists()).toBe(true)
      expect(wrapper.find('.stat-hit').text()).toContain('Hit')
    })

    it('hides stats when values are zero', () => {
      const result = createPlayResult({
        outs_recorded: 0,
        runs_scored: 0,
        is_hit: false,
      })

      const wrapper = mount(PlayResult, {
        props: { result },
      })

      expect(wrapper.find('.stat-outs').exists()).toBe(false)
      expect(wrapper.find('.stat-runs').exists()).toBe(false)
      expect(wrapper.find('.stat-hit').exists()).toBe(false)
    })
  })

  // ============================================================================
  // Runner Advancement Tests
  // ============================================================================

  describe('Runner Advancement', () => {
    it('displays runner advancement section when runners advanced', () => {
      const result = createPlayResult({
        runners_advanced: [
          { from: 1, to: 3, out: false },
        ],
      })

      const wrapper = mount(PlayResult, {
        props: { result },
      })

      expect(wrapper.find('.runner-advancement').exists()).toBe(true)
      expect(wrapper.text()).toContain('Runner Movement:')
    })

    it('hides runner advancement when no runners advanced', () => {
      const result = createPlayResult({
        runners_advanced: [],
      })

      const wrapper = mount(PlayResult, {
        props: { result },
      })

      expect(wrapper.find('.runner-advancement').exists()).toBe(false)
    })

    it('formats base labels correctly', () => {
      const result = createPlayResult({
        runners_advanced: [
          { from: 0, to: 1 }, // Batter to 1st
          { from: 1, to: 2 }, // 1st to 2nd
          { from: 2, to: 3 }, // 2nd to 3rd
          { from: 3, to: 4 }, // 3rd to Home
        ],
      })

      const wrapper = mount(PlayResult, {
        props: { result },
      })

      expect(wrapper.text()).toContain('Batter')
      expect(wrapper.text()).toContain('1st')
      expect(wrapper.text()).toContain('2nd')
      expect(wrapper.text()).toContain('3rd')
      expect(wrapper.text()).toContain('Home')
    })

    it('shows "Out" for runners who are out', () => {
      const result = createPlayResult({
        runners_advanced: [
          { from: 1, to: 2, out: true },
        ],
      })

      const wrapper = mount(PlayResult, {
        props: { result },
      })

      expect(wrapper.find('.advancement-out').exists()).toBe(true)
      expect(wrapper.text()).toContain('Out')
    })

    it('displays multiple runner advancements', () => {
      const result = createPlayResult({
        runners_advanced: [
          { from: 1, to: 3 },
          { from: 0, to: 1 },
        ],
      })

      const wrapper = mount(PlayResult, {
        props: { result },
      })

      const items = wrapper.findAll('.advancement-item')
      expect(items).toHaveLength(2)
    })
  })

  // ============================================================================
  // Dismiss Button Tests
  // ============================================================================

  describe('Dismiss Button', () => {
    it('shows dismiss button when autoHide is false', () => {
      const result = createPlayResult()
      const wrapper = mount(PlayResult, {
        props: {
          result,
          autoHide: false,
        },
      })

      expect(wrapper.find('.dismiss-button').exists()).toBe(true)
    })

    it('hides dismiss button when autoHide is true', () => {
      const result = createPlayResult()
      const wrapper = mount(PlayResult, {
        props: {
          result,
          autoHide: true,
        },
      })

      expect(wrapper.find('.dismiss-button').exists()).toBe(false)
    })

    it('emits dismiss event when button clicked', async () => {
      const result = createPlayResult()
      const wrapper = mount(PlayResult, {
        props: {
          result,
          autoHide: false,
        },
      })

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

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

  // ============================================================================
  // Auto-Hide Tests
  // ============================================================================

  describe('Auto-Hide', () => {
    it('emits dismiss after 5 seconds when autoHide is true', async () => {
      const result = createPlayResult()
      const wrapper = mount(PlayResult, {
        props: {
          result,
          autoHide: true,
        },
      })

      expect(wrapper.emitted('dismiss')).toBeFalsy()

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

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

    it('does not auto-dismiss when autoHide is false', async () => {
      const result = createPlayResult()
      const wrapper = mount(PlayResult, {
        props: {
          result,
          autoHide: false,
        },
      })

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

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

    it('does not auto-dismiss when result is null', async () => {
      const wrapper = mount(PlayResult, {
        props: {
          result: null,
          autoHide: true,
        },
      })

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

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

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

  describe('Edge Cases', () => {
    it('handles very long play descriptions', () => {
      const result = createPlayResult({
        description: 'A'.repeat(200),
      })

      const wrapper = mount(PlayResult, {
        props: { result },
      })

      expect(wrapper.find('.result-description').text()).toHaveLength(200)
    })

    it('handles high play numbers', () => {
      const result = createPlayResult({ play_number: 999 })
      const wrapper = mount(PlayResult, {
        props: { result },
      })

      expect(wrapper.text()).toContain('Play #999')
    })

    it('handles multiple outs recorded', () => {
      const result = createPlayResult({ outs_recorded: 3 })
      const wrapper = mount(PlayResult, {
        props: { result },
      })

      expect(wrapper.find('.stat-outs').text()).toContain('3')
    })

    it('handles grand slam scenario (4 runs)', () => {
      const result = createPlayResult({
        runs_scored: 4,
        is_hit: true,
        outs_recorded: 0,
      })

      const wrapper = mount(PlayResult, {
        props: { result },
      })

      expect(wrapper.find('.stat-runs').text()).toContain('4')
      expect(wrapper.find('.result-runs').exists()).toBe(true)
    })
  })
})
