import { afterEach, beforeEach, describe, expect, it } from 'vitest'
import {
  buildTransition,
  cssVariableTheme,
  getCssVariable,
  removeCssVariable,
  setCssVariable,
  useThemeCssVariables,
} from './css-variable-theme.js'

describe('css-variable-theme', () => {
  describe('cssVariableTheme', () => {
    it('should have a name property', () => {
      expect(cssVariableTheme.name).toBe('css-variable-theme')
    })

    it('should have text properties with CSS variable references', () => {
      expect(cssVariableTheme.text.primary).toBe('var(--shades-theme-text-primary)')
      expect(cssVariableTheme.text.secondary).toBe('var(--shades-theme-text-secondary)')
      expect(cssVariableTheme.text.disabled).toBe('var(--shades-theme-text-disabled)')
    })

    it('should have background properties with CSS variable references', () => {
      expect(cssVariableTheme.background.default).toBe('var(--shades-theme-background-default)')
      expect(cssVariableTheme.background.paper).toBe('var(--shades-theme-background-paper)')
    })

    it('should have palette with color variants', () => {
      expect(cssVariableTheme.palette.primary.main).toBe('var(--shades-theme-palette-primary-main)')
      expect(cssVariableTheme.palette.error.main).toBe('var(--shades-theme-palette-error-main)')
    })

    it('should have action properties with CSS variable references', () => {
      expect(cssVariableTheme.action.hoverBackground).toBe('var(--shades-theme-action-hover-background)')
      expect(cssVariableTheme.action.selectedBackground).toBe('var(--shades-theme-action-selected-background)')
      expect(cssVariableTheme.action.activeBackground).toBe('var(--shades-theme-action-active-background)')
      expect(cssVariableTheme.action.focusRing).toBe('var(--shades-theme-action-focus-ring)')
      expect(cssVariableTheme.action.focusOutline).toBe('var(--shades-theme-action-focus-outline)')
      expect(cssVariableTheme.action.disabledOpacity).toBe('var(--shades-theme-action-disabled-opacity)')
      expect(cssVariableTheme.action.backdrop).toBe('var(--shades-theme-action-backdrop)')
      expect(cssVariableTheme.action.subtleBorder).toBe('var(--shades-theme-action-subtle-border)')
    })

    it('should have shape properties with CSS variable references', () => {
      expect(cssVariableTheme.shape.borderRadius.xs).toBe('var(--shades-theme-shape-border-radius-xs)')
      expect(cssVariableTheme.shape.borderRadius.sm).toBe('var(--shades-theme-shape-border-radius-sm)')
      expect(cssVariableTheme.shape.borderRadius.md).toBe('var(--shades-theme-shape-border-radius-md)')
      expect(cssVariableTheme.shape.borderRadius.lg).toBe('var(--shades-theme-shape-border-radius-lg)')
      expect(cssVariableTheme.shape.borderRadius.full).toBe('var(--shades-theme-shape-border-radius-full)')
    })

    it('should have shadow properties with CSS variable references', () => {
      expect(cssVariableTheme.shadows.none).toBe('var(--shades-theme-shadows-none)')
      expect(cssVariableTheme.shadows.sm).toBe('var(--shades-theme-shadows-sm)')
      expect(cssVariableTheme.shadows.md).toBe('var(--shades-theme-shadows-md)')
      expect(cssVariableTheme.shadows.lg).toBe('var(--shades-theme-shadows-lg)')
      expect(cssVariableTheme.shadows.xl).toBe('var(--shades-theme-shadows-xl)')
    })

    it('should have typography properties with CSS variable references', () => {
      expect(cssVariableTheme.typography.fontFamily).toBe('var(--shades-theme-typography-font-family)')
      expect(cssVariableTheme.typography.fontSize.xs).toBe('var(--shades-theme-typography-font-size-xs)')
      expect(cssVariableTheme.typography.fontSize.md).toBe('var(--shades-theme-typography-font-size-md)')
      expect(cssVariableTheme.typography.fontSize.xxl).toBe('var(--shades-theme-typography-font-size-xxl)')
      expect(cssVariableTheme.typography.fontSize.xxxl).toBe('var(--shades-theme-typography-font-size-xxxl)')
      expect(cssVariableTheme.typography.fontSize.xxxxl).toBe('var(--shades-theme-typography-font-size-xxxxl)')
      expect(cssVariableTheme.typography.fontWeight.normal).toBe('var(--shades-theme-typography-font-weight-normal)')
      expect(cssVariableTheme.typography.fontWeight.bold).toBe('var(--shades-theme-typography-font-weight-bold)')
      expect(cssVariableTheme.typography.lineHeight.tight).toBe('var(--shades-theme-typography-line-height-tight)')
      expect(cssVariableTheme.typography.lineHeight.normal).toBe('var(--shades-theme-typography-line-height-normal)')
      expect(cssVariableTheme.typography.textShadow).toBe('var(--shades-theme-typography-text-shadow)')
    })

    it('should have transition properties with CSS variable references', () => {
      expect(cssVariableTheme.transitions.duration.fast).toBe('var(--shades-theme-transitions-duration-fast)')
      expect(cssVariableTheme.transitions.duration.normal).toBe('var(--shades-theme-transitions-duration-normal)')
      expect(cssVariableTheme.transitions.duration.slow).toBe('var(--shades-theme-transitions-duration-slow)')
      expect(cssVariableTheme.transitions.easing.default).toBe('var(--shades-theme-transitions-easing-default)')
      expect(cssVariableTheme.transitions.easing.easeOut).toBe('var(--shades-theme-transitions-easing-ease-out)')
      expect(cssVariableTheme.transitions.easing.easeInOut).toBe('var(--shades-theme-transitions-easing-ease-in-out)')
    })

    it('should have spacing properties with CSS variable references', () => {
      expect(cssVariableTheme.spacing.xs).toBe('var(--shades-theme-spacing-xs)')
      expect(cssVariableTheme.spacing.sm).toBe('var(--shades-theme-spacing-sm)')
      expect(cssVariableTheme.spacing.md).toBe('var(--shades-theme-spacing-md)')
      expect(cssVariableTheme.spacing.lg).toBe('var(--shades-theme-spacing-lg)')
      expect(cssVariableTheme.spacing.xl).toBe('var(--shades-theme-spacing-xl)')
    })
  })

  describe('setCssVariable', () => {
    let testElement: HTMLElement

    beforeEach(() => {
      testElement = document.createElement('div')
      document.body.appendChild(testElement)
    })

    afterEach(() => {
      testElement.remove()
    })

    it('should set CSS variable on element', () => {
      setCssVariable('--test-color', 'red', testElement)
      expect(testElement.style.getPropertyValue('--test-color')).toBe('red')
    })

    it('should handle var() wrapper in key name', () => {
      setCssVariable('var(--test-padding)', '10px', testElement)
      expect(testElement.style.getPropertyValue('--test-padding')).toBe('10px')
    })

    it('should set multiple CSS variables on same element', () => {
      setCssVariable('--color-a', 'blue', testElement)
      setCssVariable('--color-b', 'green', testElement)
      expect(testElement.style.getPropertyValue('--color-a')).toBe('blue')
      expect(testElement.style.getPropertyValue('--color-b')).toBe('green')
    })

    it('should override existing CSS variable', () => {
      setCssVariable('--test-value', 'first', testElement)
      setCssVariable('--test-value', 'second', testElement)
      expect(testElement.style.getPropertyValue('--test-value')).toBe('second')
    })
  })

  describe('removeCssVariable', () => {
    let testElement: HTMLElement

    beforeEach(() => {
      testElement = document.createElement('div')
      document.body.appendChild(testElement)
    })

    afterEach(() => {
      testElement.remove()
    })

    it('should remove a CSS variable from element', () => {
      testElement.style.setProperty('--test-color', 'red')
      expect(testElement.style.getPropertyValue('--test-color')).toBe('red')

      removeCssVariable('--test-color', testElement)
      expect(testElement.style.getPropertyValue('--test-color')).toBe('')
    })

    it('should handle var() wrapper in key name', () => {
      testElement.style.setProperty('--test-padding', '10px')
      removeCssVariable('var(--test-padding)', testElement)
      expect(testElement.style.getPropertyValue('--test-padding')).toBe('')
    })

    it('should not throw when removing a non-existent variable', () => {
      expect(() => removeCssVariable('--non-existent', testElement)).not.toThrow()
    })
  })

  describe('getCssVariable', () => {
    let testElement: HTMLElement

    beforeEach(() => {
      testElement = document.createElement('div')
      document.body.appendChild(testElement)
    })

    afterEach(() => {
      testElement.remove()
    })

    it('should get CSS variable from element', () => {
      testElement.style.setProperty('--test-color', 'red')
      const result = getCssVariable('--test-color', testElement)
      expect(result).toBe('red')
    })

    it('should handle var() wrapper in key name', () => {
      testElement.style.setProperty('--test-padding', '20px')
      const result = getCssVariable('var(--test-padding)', testElement)
      expect(result).toBe('20px')
    })

    it('should return empty string for non-existent variable', () => {
      const result = getCssVariable('--non-existent', testElement)
      expect(result).toBe('')
    })
  })

  describe('useThemeCssVariables', () => {
    let root: HTMLElement

    beforeEach(() => {
      root = document.documentElement
    })

    afterEach(() => {
      root.style.cssText = ''
    })

    it('should set text color CSS variables from theme', () => {
      useThemeCssVariables({
        text: {
          primary: '#ffffff',
          secondary: '#cccccc',
        },
      })

      expect(root.style.getPropertyValue('--shades-theme-text-primary')).toBe('#ffffff')
      expect(root.style.getPropertyValue('--shades-theme-text-secondary')).toBe('#cccccc')
    })

    it('should set background CSS variables from theme', () => {
      useThemeCssVariables({
        background: {
          default: '#000000',
          paper: '#111111',
        },
      })

      expect(root.style.getPropertyValue('--shades-theme-background-default')).toBe('#000000')
      expect(root.style.getPropertyValue('--shades-theme-background-paper')).toBe('#111111')
    })

    it('should set button CSS variables from theme', () => {
      useThemeCssVariables({
        button: {
          active: '#ff0000',
          hover: '#00ff00',
        },
      })

      expect(root.style.getPropertyValue('--shades-theme-button-active')).toBe('#ff0000')
      expect(root.style.getPropertyValue('--shades-theme-button-hover')).toBe('#00ff00')
    })

    it('should set deeply nested palette CSS variables from theme', () => {
      useThemeCssVariables({
        palette: {
          primary: {
            main: '#1976d2',
            light: '#42a5f5',
            dark: '#1565c0',
          },
          error: {
            main: '#d32f2f',
          },
        },
      })

      expect(root.style.getPropertyValue('--shades-theme-palette-primary-main')).toBe('#1976d2')
      expect(root.style.getPropertyValue('--shades-theme-palette-primary-light')).toBe('#42a5f5')
      expect(root.style.getPropertyValue('--shades-theme-palette-primary-dark')).toBe('#1565c0')
      expect(root.style.getPropertyValue('--shades-theme-palette-error-main')).toBe('#d32f2f')
    })

    it('should set divider CSS variable from theme', () => {
      useThemeCssVariables({
        divider: 'rgba(255, 255, 255, 0.12)',
      })

      expect(root.style.getPropertyValue('--shades-theme-divider')).toBe('rgba(255, 255, 255, 0.12)')
    })

    it('should handle partial theme with mixed nesting levels', () => {
      useThemeCssVariables({
        text: {
          primary: '#fff',
        },
        divider: '#333',
        palette: {
          success: {
            main: '#2e7d32',
          },
        },
      })

      expect(root.style.getPropertyValue('--shades-theme-text-primary')).toBe('#fff')
      expect(root.style.getPropertyValue('--shades-theme-divider')).toBe('#333')
      expect(root.style.getPropertyValue('--shades-theme-palette-success-main')).toBe('#2e7d32')
    })

    it('should allow overriding previously set CSS variables', () => {
      useThemeCssVariables({
        text: {
          primary: '#aaa',
        },
      })

      expect(root.style.getPropertyValue('--shades-theme-text-primary')).toBe('#aaa')

      useThemeCssVariables({
        text: {
          primary: '#bbb',
        },
      })

      expect(root.style.getPropertyValue('--shades-theme-text-primary')).toBe('#bbb')
    })

    it('should remove stale CSS variables not present in the new theme', () => {
      useThemeCssVariables({
        text: {
          primary: '#fff',
          secondary: '#ccc',
          disabled: '#999',
        },
        background: {
          default: '#000',
          paper: '#111',
          paperImage: 'url(texture.png)',
        },
      })

      expect(root.style.getPropertyValue('--shades-theme-text-primary')).toBe('#fff')
      expect(root.style.getPropertyValue('--shades-theme-text-secondary')).toBe('#ccc')
      expect(root.style.getPropertyValue('--shades-theme-background-paper-image')).toBe('url(texture.png)')

      useThemeCssVariables({
        text: {
          primary: '#eee',
        },
        background: {
          default: '#000',
          paper: '#222',
        },
      })

      expect(root.style.getPropertyValue('--shades-theme-text-primary')).toBe('#eee')
      expect(root.style.getPropertyValue('--shades-theme-text-secondary')).toBe('')
      expect(root.style.getPropertyValue('--shades-theme-text-disabled')).toBe('')
      expect(root.style.getPropertyValue('--shades-theme-background-default')).toBe('#000')
      expect(root.style.getPropertyValue('--shades-theme-background-paper')).toBe('#222')
      expect(root.style.getPropertyValue('--shades-theme-background-paper-image')).toBe('')
    })

    it('should remove all nested CSS variables when a whole section is omitted', () => {
      useThemeCssVariables({
        palette: {
          primary: {
            light: '#aaa',
            lightContrast: '#000',
            main: '#bbb',
            mainContrast: '#000',
            dark: '#ccc',
            darkContrast: '#fff',
          },
        },
        spacing: {
          xs: '4px',
          sm: '8px',
          md: '16px',
          lg: '24px',
          xl: '32px',
        },
      })

      expect(root.style.getPropertyValue('--shades-theme-palette-primary-main')).toBe('#bbb')
      expect(root.style.getPropertyValue('--shades-theme-spacing-md')).toBe('16px')

      useThemeCssVariables({
        spacing: {
          xs: '2px',
          sm: '4px',
          md: '8px',
          lg: '16px',
          xl: '24px',
        },
      })

      expect(root.style.getPropertyValue('--shades-theme-palette-primary-main')).toBe('')
      expect(root.style.getPropertyValue('--shades-theme-palette-primary-light')).toBe('')
      expect(root.style.getPropertyValue('--shades-theme-spacing-md')).toBe('8px')
    })

    it('should set action CSS variables from theme', () => {
      useThemeCssVariables({
        action: {
          hoverBackground: 'rgba(255, 255, 255, 0.08)',
          focusRing: '0 0 0 3px rgba(255, 255, 255, 0.15)',
        },
      })

      expect(root.style.getPropertyValue('--shades-theme-action-hover-background')).toBe('rgba(255, 255, 255, 0.08)')
      expect(root.style.getPropertyValue('--shades-theme-action-focus-ring')).toBe(
        '0 0 0 3px rgba(255, 255, 255, 0.15)',
      )
    })

    it('should set shape CSS variables from theme', () => {
      useThemeCssVariables({
        shape: {
          borderRadius: {
            md: '8px',
            full: '50%',
          },
        },
      })

      expect(root.style.getPropertyValue('--shades-theme-shape-border-radius-md')).toBe('8px')
      expect(root.style.getPropertyValue('--shades-theme-shape-border-radius-full')).toBe('50%')
    })

    it('should set typography CSS variables from theme', () => {
      useThemeCssVariables({
        typography: {
          fontFamily: 'monospace',
          fontSize: {
            md: '14px',
          },
          fontWeight: {
            bold: '700',
          },
        },
      })

      expect(root.style.getPropertyValue('--shades-theme-typography-font-family')).toBe('monospace')
      expect(root.style.getPropertyValue('--shades-theme-typography-font-size-md')).toBe('14px')
      expect(root.style.getPropertyValue('--shades-theme-typography-font-weight-bold')).toBe('700')
    })

    it('should set transition CSS variables from theme', () => {
      useThemeCssVariables({
        transitions: {
          duration: {
            fast: '0.15s',
          },
          easing: {
            default: 'ease',
          },
        },
      })

      expect(root.style.getPropertyValue('--shades-theme-transitions-duration-fast')).toBe('0.15s')
      expect(root.style.getPropertyValue('--shades-theme-transitions-easing-default')).toBe('ease')
    })

    it('should set spacing CSS variables from theme', () => {
      useThemeCssVariables({
        spacing: {
          xs: '4px',
          md: '16px',
          xl: '32px',
        },
      })

      expect(root.style.getPropertyValue('--shades-theme-spacing-xs')).toBe('4px')
      expect(root.style.getPropertyValue('--shades-theme-spacing-md')).toBe('16px')
      expect(root.style.getPropertyValue('--shades-theme-spacing-xl')).toBe('32px')
    })

    it('should set CSS variables on a custom root element instead of :root', () => {
      const customRoot = document.createElement('div')
      document.body.appendChild(customRoot)

      useThemeCssVariables(
        {
          text: { primary: '#ff0000' },
          divider: '#00ff00',
        },
        customRoot,
      )

      expect(customRoot.style.getPropertyValue('--shades-theme-text-primary')).toBe('#ff0000')
      expect(customRoot.style.getPropertyValue('--shades-theme-divider')).toBe('#00ff00')
      expect(root.style.getPropertyValue('--shades-theme-text-primary')).toBe('')

      customRoot.remove()
    })

    it('should scope nested palette variables to custom root element', () => {
      const customRoot = document.createElement('div')
      document.body.appendChild(customRoot)

      useThemeCssVariables(
        {
          palette: {
            primary: {
              main: '#1976d2',
              mainContrast: '#ffffff',
            },
          },
        },
        customRoot,
      )

      expect(customRoot.style.getPropertyValue('--shades-theme-palette-primary-main')).toBe('#1976d2')
      expect(customRoot.style.getPropertyValue('--shades-theme-palette-primary-main-contrast')).toBe('#ffffff')
      expect(root.style.getPropertyValue('--shades-theme-palette-primary-main')).toBe('')

      customRoot.remove()
    })
  })

  describe('buildTransition', () => {
    it('should build a single transition string', () => {
      expect(buildTransition(['background', '0.2s', 'ease'])).toBe('background 0.2s ease')
    })

    it('should join multiple transitions with commas', () => {
      expect(buildTransition(['background', '0.2s', 'ease'], ['opacity', '0.15s', 'ease-out'])).toBe(
        'background 0.2s ease, opacity 0.15s ease-out',
      )
    })

    it('should handle three or more transitions', () => {
      const result = buildTransition(
        ['background', '0.2s', 'ease'],
        ['color', '0.3s', 'linear'],
        ['transform', '0.1s', 'ease-in-out'],
      )
      expect(result).toBe('background 0.2s ease, color 0.3s linear, transform 0.1s ease-in-out')
    })

    it('should work with CSS variable references', () => {
      expect(
        buildTransition([
          'background',
          cssVariableTheme.transitions.duration.normal,
          cssVariableTheme.transitions.easing.default,
        ]),
      ).toBe(
        'background var(--shades-theme-transitions-duration-normal) var(--shades-theme-transitions-easing-default)',
      )
    })
  })
})
