import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import DiceShapes from '~/components/Gameplay/DiceShapes.vue'

describe('DiceShapes', () => {
  const defaultD6Props = {
    type: 'd6' as const,
    value: 5,
  }

  const defaultD20Props = {
    type: 'd20' as const,
    value: 15,
  }

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

  describe('D6 Shape Rendering', () => {
    it('renders d6 die container when type is d6', () => {
      /**
       * The d6 die should render a square-shaped die with rounded corners,
       * decorative corner dots suggesting pip positions, and the value centered.
       */
      const wrapper = mount(DiceShapes, {
        props: defaultD6Props,
      })

      expect(wrapper.find('.die-container').exists()).toBe(true)
      expect(wrapper.find('svg.die-shape').exists()).toBe(true)
    })

    it('renders rect element for d6 die body', () => {
      /**
       * D6 dice use a rounded rectangle (rect with rx/ry) to create the
       * classic square die shape with softened corners.
       */
      const wrapper = mount(DiceShapes, {
        props: defaultD6Props,
      })

      const rect = wrapper.find('rect')
      expect(rect.exists()).toBe(true)
      expect(rect.attributes('rx')).toBe('10')
      expect(rect.attributes('ry')).toBe('10')
    })

    it('renders four corner dots for d6 decoration', () => {
      /**
       * Four decorative dots in the corners suggest the pip positions
       * of a physical die, adding visual authenticity.
       */
      const wrapper = mount(DiceShapes, {
        props: defaultD6Props,
      })

      const circles = wrapper.findAll('circle')
      expect(circles).toHaveLength(4)
    })

    it('displays the value in the center', () => {
      const wrapper = mount(DiceShapes, {
        props: { ...defaultD6Props, value: 3 },
      })

      expect(wrapper.find('.die-value').text()).toBe('3')
    })

    it('displays label when provided', () => {
      const wrapper = mount(DiceShapes, {
        props: { ...defaultD6Props, label: 'd6 (One)' },
      })

      expect(wrapper.find('.die-label').exists()).toBe(true)
      expect(wrapper.find('.die-label').text()).toBe('d6 (One)')
    })

    it('displays sublabel when provided', () => {
      const wrapper = mount(DiceShapes, {
        props: { ...defaultD6Props, sublabel: '(3 + 2)' },
      })

      expect(wrapper.find('.die-sublabel').exists()).toBe(true)
      expect(wrapper.find('.die-sublabel').text()).toBe('(3 + 2)')
    })

    it('hides label when not provided', () => {
      const wrapper = mount(DiceShapes, {
        props: defaultD6Props,
      })

      expect(wrapper.find('.die-label').exists()).toBe(false)
    })
  })

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

  describe('D20 Shape Rendering', () => {
    it('renders d20 die container when type is d20', () => {
      /**
       * The d20 die renders a hexagonal shape inspired by the icosahedron
       * geometry of a real d20, with facet lines for 3D effect.
       */
      const wrapper = mount(DiceShapes, {
        props: defaultD20Props,
      })

      expect(wrapper.find('.die-container').exists()).toBe(true)
      expect(wrapper.find('svg.die-shape').exists()).toBe(true)
    })

    it('renders polygon element for d20 hexagonal shape', () => {
      /**
       * D20 uses a 6-sided polygon (hexagon) rotated with flat top
       * to suggest the multi-faceted nature of an icosahedron.
       */
      const wrapper = mount(DiceShapes, {
        props: defaultD20Props,
      })

      const polygon = wrapper.find('polygon')
      expect(polygon.exists()).toBe(true)

      // Verify points attribute contains 6 coordinate pairs
      const points = polygon.attributes('points')
      expect(points).toBeDefined()
      const pointPairs = points!.split(' ')
      expect(pointPairs).toHaveLength(6)
    })

    it('renders facet lines for 3D effect', () => {
      /**
       * Two vertical lines inside the hexagon create the illusion
       * of depth and the faceted surface of a d20.
       */
      const wrapper = mount(DiceShapes, {
        props: defaultD20Props,
      })

      const lines = wrapper.findAll('line')
      expect(lines).toHaveLength(2)
    })

    it('displays the value with large styling', () => {
      const wrapper = mount(DiceShapes, {
        props: defaultD20Props,
      })

      const valueEl = wrapper.find('.die-value')
      expect(valueEl.text()).toBe('15')
      expect(valueEl.classes()).toContain('die-value-large')
    })

    it('displays label when provided', () => {
      const wrapper = mount(DiceShapes, {
        props: { ...defaultD20Props, label: 'Resolution' },
      })

      expect(wrapper.find('.die-label').text()).toBe('Resolution')
    })
  })

  // ============================================================================
  // Color Calculation Tests
  // ============================================================================

  describe('Color Calculations', () => {
    it('applies fill color from color prop', () => {
      /**
       * The color prop (hex without #) should be converted to a proper
       * CSS color value and applied to the die fill.
       */
      const wrapper = mount(DiceShapes, {
        props: { ...defaultD6Props, color: '0066ff' },
      })

      const rect = wrapper.find('rect')
      expect(rect.attributes('fill')).toBe('#0066ff')
    })

    it('uses default red color when no color prop provided', () => {
      /**
       * Default dice color is cc0000 (red) matching the traditional
       * Strat-O-Matic dice color.
       */
      const wrapper = mount(DiceShapes, {
        props: defaultD6Props,
      })

      const rect = wrapper.find('rect')
      expect(rect.attributes('fill')).toBe('#cc0000')
    })

    it('calculates darker stroke color from fill', () => {
      /**
       * The stroke color should be a darkened version of the fill
       * to create depth and definition around the die edge.
       */
      const wrapper = mount(DiceShapes, {
        props: { ...defaultD6Props, color: 'ffffff' },
      })

      const rect = wrapper.find('rect')
      const stroke = rect.attributes('stroke')
      // White (ffffff) darkened by 20% should be #cccccc
      expect(stroke).toBe('#cccccc')
    })

    it('uses white text on dark backgrounds', () => {
      /**
       * When the die color is dark (low luminance), the value text
       * should be white for readability.
       */
      const wrapper = mount(DiceShapes, {
        props: { ...defaultD6Props, color: '000000' },
      })

      const valueEl = wrapper.find('.die-value')
      expect(valueEl.attributes('style')).toContain('color: #ffffff')
    })

    it('uses dark text on light backgrounds', () => {
      /**
       * When the die color is light (high luminance), the value text
       * should be dark (#1a1a1a) for readability.
       */
      const wrapper = mount(DiceShapes, {
        props: { ...defaultD6Props, color: 'ffffff' },
      })

      const valueEl = wrapper.find('.die-value')
      expect(valueEl.attributes('style')).toContain('color: #1a1a1a')
    })

    it('uses white corner dots on dark d6 backgrounds', () => {
      const wrapper = mount(DiceShapes, {
        props: { ...defaultD6Props, color: '000066' },
      })

      const circles = wrapper.findAll('circle')
      expect(circles[0].attributes('fill')).toBe('#ffffff')
    })

    it('uses dark corner dots on light d6 backgrounds', () => {
      const wrapper = mount(DiceShapes, {
        props: { ...defaultD6Props, color: 'ffff00' },
      })

      const circles = wrapper.findAll('circle')
      expect(circles[0].attributes('fill')).toBe('#333333')
    })
  })

  // ============================================================================
  // Size Prop Tests
  // ============================================================================

  describe('Size Configuration', () => {
    it('applies default size of 100px', () => {
      const wrapper = mount(DiceShapes, {
        props: defaultD6Props,
      })

      const container = wrapper.find('.die-container')
      expect(container.attributes('style')).toContain('width: 100px')
      expect(container.attributes('style')).toContain('height: 100px')
    })

    it('applies custom size from prop', () => {
      const wrapper = mount(DiceShapes, {
        props: { ...defaultD6Props, size: 80 },
      })

      const container = wrapper.find('.die-container')
      expect(container.attributes('style')).toContain('width: 80px')
      expect(container.attributes('style')).toContain('height: 80px')
    })

    it('scales SVG viewBox to match size', () => {
      const wrapper = mount(DiceShapes, {
        props: { ...defaultD6Props, size: 120 },
      })

      const svg = wrapper.find('svg')
      expect(svg.attributes('viewBox')).toBe('0 0 120 120')
    })

    it('scales d6 rect dimensions proportionally', () => {
      const wrapper = mount(DiceShapes, {
        props: { ...defaultD6Props, size: 80 },
      })

      const rect = wrapper.find('rect')
      // width and height should be size - 8
      expect(rect.attributes('width')).toBe('72')
      expect(rect.attributes('height')).toBe('72')
    })
  })

  // ============================================================================
  // Hexagon Point Calculation Tests
  // ============================================================================

  describe('Hexagon Point Calculation', () => {
    it('generates valid hexagon points for d20', () => {
      /**
       * The hexagon points should form a valid 6-sided polygon
       * with coordinates that create a symmetrical shape.
       */
      const wrapper = mount(DiceShapes, {
        props: { ...defaultD20Props, size: 100 },
      })

      const polygon = wrapper.find('polygon')
      const points = polygon.attributes('points')!
      const pointPairs = points.split(' ')

      // Each pair should be valid x,y coordinates
      pointPairs.forEach(pair => {
        const [x, y] = pair.split(',').map(Number)
        expect(x).toBeGreaterThanOrEqual(0)
        expect(x).toBeLessThanOrEqual(100)
        expect(y).toBeGreaterThanOrEqual(0)
        expect(y).toBeLessThanOrEqual(100)
      })
    })

    it('scales hexagon points with size prop', () => {
      const smallWrapper = mount(DiceShapes, {
        props: { ...defaultD20Props, size: 50 },
      })
      const largeWrapper = mount(DiceShapes, {
        props: { ...defaultD20Props, size: 100 },
      })

      const smallPoints = smallWrapper.find('polygon').attributes('points')!
      const largePoints = largeWrapper.find('polygon').attributes('points')!

      // First point of small should be roughly half the large
      const smallFirst = smallPoints.split(' ')[0].split(',').map(Number)
      const largeFirst = largePoints.split(' ')[0].split(',').map(Number)

      expect(smallFirst[0]).toBeCloseTo(largeFirst[0] / 2, 0)
    })
  })

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

  describe('Edge Cases', () => {
    it('handles string value prop', () => {
      const wrapper = mount(DiceShapes, {
        props: { ...defaultD6Props, value: '20' },
      })

      expect(wrapper.find('.die-value').text()).toBe('20')
    })

    it('handles very dark colors (near black)', () => {
      const wrapper = mount(DiceShapes, {
        props: { ...defaultD6Props, color: '0a0a0a' },
      })

      const valueEl = wrapper.find('.die-value')
      expect(valueEl.attributes('style')).toContain('color: #ffffff')
    })

    it('handles very light colors (near white)', () => {
      const wrapper = mount(DiceShapes, {
        props: { ...defaultD6Props, color: 'f5f5f5' },
      })

      const valueEl = wrapper.find('.die-value')
      expect(valueEl.attributes('style')).toContain('color: #1a1a1a')
    })

    it('handles mid-luminance colors correctly', () => {
      /**
       * Colors near the 50% luminance threshold should still pick
       * appropriate contrast. 808080 (gray) has exactly 50% luminance.
       */
      const wrapper = mount(DiceShapes, {
        props: { ...defaultD6Props, color: '808080' },
      })

      // Gray is exactly at threshold, should use white text
      const valueEl = wrapper.find('.die-value')
      expect(valueEl.attributes('style')).toBeDefined()
    })

    it('handles team colors correctly - red team', () => {
      const wrapper = mount(DiceShapes, {
        props: { ...defaultD6Props, color: 'cc0000' },
      })

      const rect = wrapper.find('rect')
      expect(rect.attributes('fill')).toBe('#cc0000')
      // Text should be white on red
      const valueEl = wrapper.find('.die-value')
      expect(valueEl.attributes('style')).toContain('color: #ffffff')
    })

    it('handles team colors correctly - blue team', () => {
      const wrapper = mount(DiceShapes, {
        props: { ...defaultD6Props, color: '0066cc' },
      })

      const rect = wrapper.find('rect')
      expect(rect.attributes('fill')).toBe('#0066cc')
      // Text should be white on blue
      const valueEl = wrapper.find('.die-value')
      expect(valueEl.attributes('style')).toContain('color: #ffffff')
    })

    it('handles team colors correctly - yellow team', () => {
      const wrapper = mount(DiceShapes, {
        props: { ...defaultD6Props, color: 'ffcc00' },
      })

      const rect = wrapper.find('rect')
      expect(rect.attributes('fill')).toBe('#ffcc00')
      // Text should be dark on yellow
      const valueEl = wrapper.find('.die-value')
      expect(valueEl.attributes('style')).toContain('color: #1a1a1a')
    })
  })

  // ============================================================================
  // Slot Content Tests
  // ============================================================================

  describe('Slot Content', () => {
    it('renders slot content instead of value when provided', () => {
      /**
       * The die-value slot allows custom content to be rendered
       * instead of the raw number value.
       */
      const wrapper = mount(DiceShapes, {
        props: defaultD6Props,
        slots: {
          default: '<span class="custom-value">★</span>',
        },
      })

      expect(wrapper.find('.custom-value').exists()).toBe(true)
      expect(wrapper.find('.custom-value').text()).toBe('★')
    })

    it('falls back to value prop when no slot content', () => {
      const wrapper = mount(DiceShapes, {
        props: { ...defaultD6Props, value: 6 },
      })

      expect(wrapper.find('.die-value').text()).toBe('6')
    })
  })
})
