import { describe, it, expect } from 'vitest'
import { createAuditEntry, digestInput, formatAuditLine, parseAuditJsonl } from '../audit.js'

describe('digestInput', () => {
  it('produces a deterministic hash for the same value', () => {
    const a = digestInput({ foo: 1, bar: 'baz' })
    const b = digestInput({ foo: 1, bar: 'baz' })
    expect(a).toBe(b)
  })

  it('is key-order stable', () => {
    const a = digestInput({ a: 1, b: 2 })
    const b = digestInput({ b: 2, a: 1 })
    expect(a).toBe(b)
  })

  it('changes when input changes', () => {
    expect(digestInput({ x: 1 })).not.toBe(digestInput({ x: 2 }))
  })

  it('handles null/undefined input gracefully', () => {
    expect(digestInput(null)).toMatch(/^[0-9a-f]{64}$/)
    expect(digestInput(undefined)).toMatch(/^[0-9a-f]{64}$/)
  })
})

describe('createAuditEntry', () => {
  const base = {
    sessionId: 'sess-1',
    toolName: 'Bash',
    input: { command: 'ls' },
    securityMode: false,
    model: 'glm-5.1',
    now: 1700000000000,
  }

  it('builds a v1 entry', () => {
    const e = createAuditEntry(base)
    expect(e.version).toBe(1)
    expect(e.tool_name).toBe('Bash')
    expect(e.session_id).toBe('sess-1')
    expect(e.input_digest).toMatch(/^[0-9a-f]{64}$/)
    expect(e.security_mode).toBe(false)
    expect(e.model).toBe('glm-5.1')
    expect(e.timestamp).toBe(1700000000000)
  })

  it('gives each entry a unique id', () => {
    const a = createAuditEntry(base)
    const b = createAuditEntry(base)
    expect(a.id).not.toBe(b.id)
  })

  it('records the security_mode flag as captured at call time', () => {
    const on = createAuditEntry({ ...base, securityMode: true })
    expect(on.security_mode).toBe(true)
  })
})

describe('parseAuditJsonl', () => {
  it('parses valid entries', () => {
    const e = createAuditEntry({
      sessionId: 's',
      toolName: 'Read',
      input: { file_path: '/a' },
      securityMode: false,
    })
    const blob = JSON.stringify(e) + '\n' + JSON.stringify(e) + '\n'
    expect(parseAuditJsonl(blob)).toHaveLength(2)
  })

  it('skips malformed lines', () => {
    const blob = 'not json\n\n{}\n'
    expect(parseAuditJsonl(blob)).toHaveLength(0)
  })

  it('rejects non-v1 entries defensively', () => {
    const blob =
      '{"version":2,"id":"x","timestamp":0,"session_id":"s","tool_name":"x","input_digest":"' +
      'x'.repeat(64) +
      '","security_mode":false}\n'
    expect(parseAuditJsonl(blob)).toHaveLength(0)
  })
})

describe('formatAuditLine', () => {
  const entry = createAuditEntry({
    sessionId: 's',
    toolName: 'Bash',
    input: { command: 'git status' },
    securityMode: true,
    model: 'glm-5.1',
    now: Date.UTC(2026, 3, 17, 10, 30, 0),
  })

  it('includes the tool name', () => {
    expect(formatAuditLine(entry)).toContain('Bash')
  })

  it('includes a truncated input digest', () => {
    expect(formatAuditLine(entry)).toMatch(/[0-9a-f]{8}/)
  })

  it('flags security_mode on with a visible tag', () => {
    expect(formatAuditLine(entry)).toContain('[security-mode]')
  })

  it('does not add the tag when security mode was off', () => {
    const off = createAuditEntry({
      sessionId: 's',
      toolName: 'Read',
      input: {},
      securityMode: false,
      now: 0,
    })
    expect(formatAuditLine(off)).not.toContain('[security-mode]')
  })
})
