import { describe, expect, it } from 'vitest'
import { ConfigError, configField, defineConfig, readConfig } from './config.js'

describe('readConfig', () => {
  const schema = defineConfig({
    DATABASE_URL: configField.url(),
    PORT: configField.number({ default: 3000 }),
    ENABLED: configField.boolean({ default: false }),
    LABEL: configField.string({ optional: true }),
  })

  it('parses typed values, defaults, and optional fields', () => {
    const result = readConfig(schema, {
      DATABASE_URL: 'https://example.test/db',
      PORT: '8080',
      ENABLED: 'true',
    })
    expect(result).toEqual({
      DATABASE_URL: 'https://example.test/db',
      PORT: 8080,
      ENABLED: true,
      LABEL: undefined,
    })
  })

  it('throws contextual missing errors', () => {
    expect(() => readConfig(schema, {})).toThrowError(ConfigError)
    try {
      readConfig(schema, {})
    } catch (error) {
      expect(error).toMatchObject({ key: 'DATABASE_URL', code: 'missing' })
    }
  })

  it.each([
    ['PORT', 'Infinity'],
    ['PORT', 'not-a-number'],
    ['ENABLED', 'yes'],
    ['DATABASE_URL', 'not a url'],
  ])('fails closed for invalid %s', (key, value) => {
    const env = { DATABASE_URL: 'https://example.test', [key]: value }
    expect(() => readConfig(schema, env)).toThrowError(ConfigError)
  })

  it('never retains a rejected raw secret or arbitrary parser exception', () => {
    const secret = 'postgres://user:super-secret@example.test/db'
    const secretSchema = defineConfig({
      DATABASE_URL: {
        parse(raw: string): string {
          const parserError = new Error(`Rejected secret: ${raw}`)
          Object.assign(parserError, { raw, nested: { raw } })
          throw parserError
        },
      },
    })

    try {
      readConfig(secretSchema, { DATABASE_URL: secret })
      throw new Error('expected failure')
    } catch (error) {
      expect(error).toBeInstanceOf(ConfigError)
      expect(error).toMatchObject({ key: 'DATABASE_URL', code: 'invalid' })
      expect(String(error)).not.toContain(secret)
      expect((error as Error).cause).toBeUndefined()
      expect(JSON.stringify(error)).not.toContain(secret)
      expect(Object.values(error as object).join(' ')).not.toContain(secret)
    }
  })

  it('accepts only explicit boolean forms', () => {
    const bool = defineConfig({ FLAG: configField.boolean() })
    expect(readConfig(bool, { FLAG: 'false' }).FLAG).toBe(false)
    expect(() => readConfig(bool, { FLAG: '0' })).toThrowError(ConfigError)
  })
})
