import { describe, expect, it } from 'vitest'
import { canonicalHash, canonicalize } from '../canonical-hash.js'

describe('canonicalHash', () => {
  it('is deterministic — same input yields the same hash (test 1)', () => {
    const spec = { screenCode: 'SCR-X-001', entity: 'TypeAffaire', view: 'list', columns: [{ key: 'code' }] }
    expect(canonicalHash(spec)).toBe(canonicalHash(spec))
    expect(canonicalHash(spec)).toMatch(/^sha256-/)
  })

  it('is insensitive to object key order (test 2)', () => {
    const a = { entity: 'TypeAffaire', view: 'list', permission: 'gaf.x.read' }
    const b = { permission: 'gaf.x.read', view: 'list', entity: 'TypeAffaire' }
    expect(canonicalHash(a)).toBe(canonicalHash(b))
  })

  it('is sensitive to value changes (test 3)', () => {
    const base = { entity: 'TypeAffaire', view: 'list', columns: [{ key: 'code', labelKey: 'list.columns.code' }] }
    const changed = { entity: 'TypeAffaire', view: 'list', columns: [{ key: 'code', labelKey: 'list.columns.libelle' }] }
    expect(canonicalHash(base)).not.toBe(canonicalHash(changed))
  })

  it('treats array order as significant (display order matters)', () => {
    expect(canonicalHash({ columns: [{ key: 'a' }, { key: 'b' }] })).not.toBe(
      canonicalHash({ columns: [{ key: 'b' }, { key: 'a' }] }),
    )
  })

  it('ignores undefined values (an absent key === an explicit undefined)', () => {
    expect(canonicalHash({ a: 1, b: undefined })).toBe(canonicalHash({ a: 1 }))
  })
})

describe('canonicalize', () => {
  it('sorts nested object keys recursively', () => {
    const out = canonicalize({ b: { d: 2, c: 1 }, a: 0 }) as Record<string, unknown>
    expect(JSON.stringify(out)).toBe('{"a":0,"b":{"c":1,"d":2}}')
  })

  it('passes primitives and null through untouched', () => {
    expect(canonicalize('s')).toBe('s')
    expect(canonicalize(42)).toBe(42)
    expect(canonicalize(null)).toBeNull()
  })
})
