import { Type } from 'typebox'
import { Fail, Ok } from './~validate.ts'
import { Assert } from 'test'

const Test = Assert.Context('Value.Check.Union')

Test('Should validate union of string, number and boolean', () => {
  const A = Type.String()
  const B = Type.Number()
  const C = Type.Boolean()
  const T = Type.Union([A, B, C])
  Ok(T, 'hello')
  Ok(T, true)
  Ok(T, 42)
})
Test('Should validate union of objects', () => {
  const A = Type.Object({ a: Type.String() }, { additionalProperties: false })
  const B = Type.Object({ b: Type.String() }, { additionalProperties: false })
  const T = Type.Union([A, B])
  Ok(T, { a: 'hello' })
  Ok(T, { b: 'world' })
})
Test('Should fail to validate for descriminated union types', () => {
  const A = Type.Object({ kind: Type.Literal('A'), value: Type.String() })
  const B = Type.Object({ kind: Type.Literal('B'), value: Type.Number() })
  const T = Type.Union([A, B])
  Fail(T, { kind: 'A', value: 42 }) // expect { kind: 'A', value: string }
  Fail(T, { kind: 'B', value: 'hello' }) // expect { kind: 'B', value: number }
})
Test('Should validate union of objects where properties overlap', () => {
  const A = Type.Object({ a: Type.String() }, { additionalProperties: false })
  const B = Type.Object({ a: Type.String(), b: Type.String() }, { additionalProperties: false })
  const T = Type.Union([A, B])
  Ok(T, { a: 'hello' }) // A
  Ok(T, { a: 'hello', b: 'world' }) // B
})
Test('Should validate union of overlapping property of varying type', () => {
  const A = Type.Object({ a: Type.String(), b: Type.Number() }, { additionalProperties: false })
  const B = Type.Object({ a: Type.String(), b: Type.String() }, { additionalProperties: false })
  const T = Type.Union([A, B])
  Ok(T, { a: 'hello', b: 42 }) // A
  Ok(T, { a: 'hello', b: 'world' }) // B
})
Test('Should validate union of literal strings', () => {
  const A = Type.Literal('hello')
  const B = Type.Literal('world')
  const T = Type.Union([A, B])
  Ok(T, 'hello') // A
  Ok(T, 'world') // B
})
Test('Should not validate union of literal strings for unknown string', () => {
  const A = Type.Literal('hello')
  const B = Type.Literal('world')
  const T = Type.Union([A, B])
  Fail(T, 'foo') // A
  Fail(T, 'bar') // B
})
