import type { RecipeDefinition } from '@bamboocss/types'
import { describe, expect, test } from 'vitest'
import { createRuleProcessor } from './fixture'

function processAtomicRule(config: RecipeDefinition<any>) {
  return createRuleProcessor().cva(config).toCss()
}

describe('Atomic recipe', () => {
  test('should work', () => {
    const sheet = processAtomicRule({
      base: {
        fontSize: 'lg',
      },
      variants: {
        size: {
          sm: {
            padding: '2',
            borderRadius: 'sm',
          },
          md: {
            padding: '4',
            borderRadius: 'md',
          },
        },
        variant: {
          primary: {
            color: 'white',
            backgroundColor: 'blue.500',
          },
          danger: {
            color: 'white',
            backgroundColor: 'red.500',
            _hover: {
              color: 'green',
            },
          },
        },
      },
    })

    expect(sheet).toMatchInlineSnapshot(`
      "@layer recipes {
        .cva_jgZhlc {
          font-size: var(--font-sizes-lg);
      }

        .cva_jgZhlc--size_sm {
          padding: var(--spacing-2);
          border-radius: var(--radii-sm);
      }

        .cva_jgZhlc--size_md {
          padding: var(--spacing-4);
          border-radius: var(--radii-md);
      }

        .cva_jgZhlc--variant_primary {
          color: var(--colors-white);
          background-color: var(--colors-blue-500);
      }

        .cva_jgZhlc--variant_danger {
          color: var(--colors-white);
          background-color: var(--colors-red-500);
      }

        .cva_jgZhlc--variant_danger:is(:hover, [data-hover]) {
          color: green;
      }
      }"
    `)
  })
})

/**
 * A recipe's variant value is a key of the `variants` object, so it is a string by
 * construction — and the propKey it is stored under keeps it as written.
 *
 * The decoder used to reinterpret it with `parseValue`, which coerces anything `Number()`
 * accepts. `'1.0'`, `'1e3'` and `'0x10'` came back as `1`, `1000` and `16`, the lookup
 * missed, and the rule was dropped while the runtime went on asking for `--size_1.0`.
 * Canonical numerics round-tripped, which is why it held together at all.
 */
describe('variant values that Number() would reinterpret', () => {
  const emitted = (values: string[]) =>
    createRuleProcessor()
      .cva({
        className: 'rt',
        variants: { size: Object.fromEntries(values.map((value, index) => [value, { padding: String(index + 1) }])) },
      } as never)
      .getClassNames()

  test.each([['1.0'], ['1e3'], ['0x10'], ['+1'], ['.5'], ['1_000'], ['Infinity']])(
    'a variant valued %s still emits a rule',
    (value) => {
      // Escaped for the selector; the class attribute keeps the raw value.
      expect(emitted([value]).map((className) => className.replaceAll('\\', ''))).toContain(`rt--size_${value}`)
    },
  )

  test('canonical numerics and booleans are unaffected', () => {
    expect(emitted(['0', '1', '01', 'true', 'false'])).toEqual([
      'rt--size_0',
      'rt--size_1',
      'rt--size_01',
      'rt--size_true',
      'rt--size_false',
    ])
  })
})
