import { describe, expect, it } from 'vite-plus/test'

import { calculateEdgeDeduction, isNearEdge, normalizeEdgeDetection } from './edgeDetection.js'

describe('normalizeEdgeDetection', () => {
  describe('presets', () => {
    it('should return default config for undefined', () => {
      const result = normalizeEdgeDetection(undefined)

      expect(result).toEqual({
        edges: ['left', 'top'],
        threshold: 12,
        strength: 500,
      })
    })

    it('should return left preset config', () => {
      const result = normalizeEdgeDetection('left')

      expect(result).toEqual({
        edges: ['left', 'top'],
        threshold: 12,
        strength: 500,
      })
    })

    it('should return right preset config', () => {
      const result = normalizeEdgeDetection('right')

      expect(result).toEqual({
        edges: ['right', 'top'],
        threshold: 12,
        strength: 500,
      })
    })

    it('should return both preset config', () => {
      const result = normalizeEdgeDetection('both')

      expect(result).toEqual({
        edges: ['left', 'right', 'top'],
        threshold: 12,
        strength: 500,
      })
    })

    it('should return none preset config', () => {
      const result = normalizeEdgeDetection('none')

      expect(result).toEqual({
        edges: [],
        threshold: 0,
        strength: 0,
      })
    })
  })

  describe('custom config', () => {
    it('should merge partial config with defaults', () => {
      const result = normalizeEdgeDetection({ threshold: 20 })

      expect(result).toEqual({
        edges: ['left', 'top'],
        threshold: 20,
        strength: 500,
      })
    })

    it('should merge partial config with custom edges', () => {
      const result = normalizeEdgeDetection({ edges: ['bottom'] })

      expect(result).toEqual({
        edges: ['bottom'],
        threshold: 12,
        strength: 500,
      })
    })

    it('should merge partial config with custom strength', () => {
      const result = normalizeEdgeDetection({ strength: 250 })

      expect(result).toEqual({
        edges: ['left', 'top'],
        threshold: 12,
        strength: 250,
      })
    })

    it('should accept full custom config', () => {
      const result = normalizeEdgeDetection({
        edges: ['right', 'bottom'],
        threshold: 30,
        strength: 1000,
      })

      expect(result).toEqual({
        edges: ['right', 'bottom'],
        threshold: 30,
        strength: 1000,
      })
    })

    it('should handle negative threshold values', () => {
      const result = normalizeEdgeDetection({ threshold: -16 })

      expect(result).toEqual({
        edges: ['left', 'top'],
        threshold: -16,
        strength: 500,
      })
    })
  })
})

describe('isNearEdge', () => {
  const mockElement = {
    getBoundingClientRect: () => ({
      left: 100,
      right: 300,
      top: 50,
      bottom: 150,
      width: 200,
      height: 100,
      x: 100,
      y: 50,
      toJSON: () => ({}),
    }),
  } as HTMLElement

  describe('left edge', () => {
    const config = { edges: ['left' as const], threshold: 12, strength: 500 }

    it('should return true when cursor is near left edge', () => {
      const result = isNearEdge({ x: 105, y: 100 }, mockElement, config)

      expect(result).toBe(true)
    })

    it('should return false when cursor is far from left edge', () => {
      const result = isNearEdge({ x: 150, y: 100 }, mockElement, config)

      expect(result).toBe(false)
    })

    it('should return true when cursor is exactly at threshold', () => {
      const result = isNearEdge({ x: 111, y: 100 }, mockElement, config)

      expect(result).toBe(true)
    })

    it('should return false when cursor is just beyond threshold', () => {
      const result = isNearEdge({ x: 113, y: 100 }, mockElement, config)

      expect(result).toBe(false)
    })
  })

  describe('right edge', () => {
    const config = { edges: ['right' as const], threshold: 12, strength: 500 }

    it('should return true when cursor is near right edge', () => {
      const result = isNearEdge({ x: 295, y: 100 }, mockElement, config)

      expect(result).toBe(true)
    })

    it('should return false when cursor is far from right edge', () => {
      const result = isNearEdge({ x: 200, y: 100 }, mockElement, config)

      expect(result).toBe(false)
    })
  })

  describe('top edge', () => {
    const config = { edges: ['top' as const], threshold: 12, strength: 500 }

    it('should return true when cursor is near top edge', () => {
      const result = isNearEdge({ x: 200, y: 55 }, mockElement, config)

      expect(result).toBe(true)
    })

    it('should return false when cursor is far from top edge', () => {
      const result = isNearEdge({ x: 200, y: 100 }, mockElement, config)

      expect(result).toBe(false)
    })
  })

  describe('bottom edge', () => {
    const config = { edges: ['bottom' as const], threshold: 12, strength: 500 }

    it('should return true when cursor is near bottom edge', () => {
      const result = isNearEdge({ x: 200, y: 145 }, mockElement, config)

      expect(result).toBe(true)
    })

    it('should return false when cursor is far from bottom edge', () => {
      const result = isNearEdge({ x: 200, y: 100 }, mockElement, config)

      expect(result).toBe(false)
    })
  })

  describe('multiple edges', () => {
    const config = { edges: ['left' as const, 'top' as const], threshold: 12, strength: 500 }

    it('should return true when near any configured edge', () => {
      expect(isNearEdge({ x: 105, y: 100 }, mockElement, config)).toBe(true)
      expect(isNearEdge({ x: 200, y: 55 }, mockElement, config)).toBe(true)
    })

    it('should return false when not near any configured edge', () => {
      const result = isNearEdge({ x: 200, y: 100 }, mockElement, config)

      expect(result).toBe(false)
    })
  })

  describe('empty edges', () => {
    const config = { edges: [], threshold: 12, strength: 500 }

    it('should return false when edges array is empty', () => {
      const result = isNearEdge({ x: 100, y: 50 }, mockElement, config)

      expect(result).toBe(false)
    })
  })

  describe('negative threshold', () => {
    const config = { edges: ['left' as const], threshold: -10, strength: 500 }

    it('should require cursor to be further inside with negative threshold', () => {
      // At left edge (100), with -10 threshold, cursor at 105 is not "near"
      // because 105 - 100 = 5, which is not < -10
      expect(isNearEdge({ x: 105, y: 100 }, mockElement, config)).toBe(false)
      expect(isNearEdge({ x: 85, y: 100 }, mockElement, config)).toBe(true)
    })
  })
})

describe('calculateEdgeDeduction', () => {
  const mockElement = {
    getBoundingClientRect: () => ({
      left: 100,
      right: 300,
      top: 50,
      bottom: 150,
      width: 200,
      height: 100,
      x: 100,
      y: 50,
      toJSON: () => ({}),
    }),
  } as HTMLElement

  const config = { edges: ['left' as const], threshold: 12, strength: 500 }

  it('should return 0 when element is null', () => {
    const result = calculateEdgeDeduction({ x: 105, y: 100 }, null, config, 2)

    expect(result).toBe(0)
  })

  it('should return 0 when edges array is empty', () => {
    const emptyConfig = { edges: [], threshold: 12, strength: 500 }
    const result = calculateEdgeDeduction({ x: 105, y: 100 }, mockElement, emptyConfig, 2)

    expect(result).toBe(0)
  })

  it('should return 0 when cursor is not near edge', () => {
    const result = calculateEdgeDeduction({ x: 200, y: 100 }, mockElement, config, 2)

    expect(result).toBe(0)
  })

  it('should return strength * depth when cursor is near edge', () => {
    const result = calculateEdgeDeduction({ x: 105, y: 100 }, mockElement, config, 2)

    expect(result).toBe(1000) // 500 * 2
  })

  it('should scale deduction with depth', () => {
    expect(calculateEdgeDeduction({ x: 105, y: 100 }, mockElement, config, 1)).toBe(500)
    expect(calculateEdgeDeduction({ x: 105, y: 100 }, mockElement, config, 2)).toBe(1000)
    expect(calculateEdgeDeduction({ x: 105, y: 100 }, mockElement, config, 3)).toBe(1500)
  })

  it('should use custom strength value', () => {
    const customConfig = { edges: ['left' as const], threshold: 12, strength: 250 }
    const result = calculateEdgeDeduction({ x: 105, y: 100 }, mockElement, customConfig, 2)

    expect(result).toBe(500) // 250 * 2
  })
})
