// KAR-906 / P3.2 — bilingual PlausibilityIssue.explanation encode/decode.

import { describe, expect, it } from 'vitest'
import { decodeBilingual, encodeBilingual, joinMissingReasons, missingFieldReason } from '../bilingual-message'

describe('encodeBilingual / decodeBilingual', () => {
  it('returns the DE string unchanged when no EN is given', () => {
    const encoded = encodeBilingual('Pflichtfeld ist leer.')
    expect(encoded).toBe('Pflichtfeld ist leer.')
  })

  it('returns the DE string unchanged when EN equals DE (nothing gained)', () => {
    const encoded = encodeBilingual('FCA', 'FCA')
    expect(encoded).toBe('FCA')
  })

  it('round-trips a genuine DE/EN pair through encode -> decode', () => {
    const encoded = encodeBilingual('Pflichtfeld „Positionsnummer" ist leer.', 'Required field "Item number" is empty.')
    const decoded = decodeBilingual(encoded)
    expect(decoded).toEqual({
      de: 'Pflichtfeld „Positionsnummer" ist leer.',
      en: 'Required field "Item number" is empty.',
    })
  })

  it('preserves real umlauts through the round-trip', () => {
    const de = 'Abschnitt „Fertigungskosten" existiert nur in NEU, nicht in ALT — Änderung prüfen.'
    const en = 'Section "Fertigungskosten" exists only in NEU, not in ALT — please review.'
    const decoded = decodeBilingual(encodeBilingual(de, en))
    expect(decoded.de).toBe(de)
    expect(decoded.en).toBe(en)
  })

  it('treats legacy plain-German text (no marker, pre-KAR-906 row) as DE with EN falling back to DE', () => {
    const decoded = decodeBilingual('Teilebenennung weicht zwischen ALT und NEU ab.')
    expect(decoded).toEqual({
      de: 'Teilebenennung weicht zwischen ALT und NEU ab.',
      en: 'Teilebenennung weicht zwischen ALT und NEU ab.',
    })
  })

  it('is tolerant of null/undefined/empty input', () => {
    expect(decodeBilingual(null)).toEqual({ de: '', en: '' })
    expect(decodeBilingual(undefined)).toEqual({ de: '', en: '' })
    expect(decodeBilingual('')).toEqual({ de: '', en: '' })
  })

  it('falls back to legacy handling on a corrupted/truncated envelope instead of throwing', () => {
    const corrupted = encodeBilingual('DE-Text', 'EN-Text').slice(0, 10)
    expect(() => decodeBilingual(corrupted)).not.toThrow()
    const decoded = decodeBilingual(corrupted)
    expect(decoded.de).toBe(corrupted)
    expect(decoded.en).toBe(corrupted)
  })

  it('falls back to DE when the encoded envelope has no en field', () => {
    // Simulates a hand-rolled/older envelope shape — decode must not throw
    // and must use de for en too.
    const raw = '@@BI1@@' + JSON.stringify({ de: 'Nur Deutsch.' })
    const decoded = decodeBilingual(raw)
    expect(decoded).toEqual({ de: 'Nur Deutsch.', en: 'Nur Deutsch.' })
  })
})

describe('missingFieldReason', () => {
  it('builds the "empty or not numeric" variant', () => {
    const reason = missingFieldReason('Rohstoffnotierung Ro [AW/kg]', 'Raw material quotation Ro [AW/kg]', false)
    expect(reason.de).toBe('Rohstoffnotierung Ro [AW/kg] ist leer oder nicht als Zahl erkennbar')
    expect(reason.en).toBe('Raw material quotation Ro [AW/kg] is empty or not recognizable as a number')
  })

  it('builds the "marked not applicable" variant', () => {
    const reason = missingFieldReason('Bezugsgewicht', 'Reference weight', true)
    expect(reason.de).toBe('Bezugsgewicht ist im Excel als "nicht anwendbar" markiert')
    expect(reason.en).toBe('Reference weight is marked "not applicable" in the Excel file')
  })
})

describe('joinMissingReasons', () => {
  it('joins multiple fragments into one bilingual nicht_pruefbar sentence', () => {
    const joined = joinMissingReasons([
      missingFieldReason('Ro', 'Ro', false),
      missingFieldReason('Bezugsgewicht', 'Reference weight', true),
    ])
    expect(joined.de).toBe('Ro ist leer oder nicht als Zahl erkennbar; Bezugsgewicht ist im Excel als "nicht anwendbar" markiert — Nachrechnung nicht moeglich.')
    expect(joined.en).toBe('Ro is empty or not recognizable as a number; Reference weight is marked "not applicable" in the Excel file — recomputation not possible.')
  })

  it('handles a single fragment', () => {
    const joined = joinMissingReasons([missingFieldReason('X', 'X', false)])
    expect(joined.de).toBe('X ist leer oder nicht als Zahl erkennbar — Nachrechnung nicht moeglich.')
    expect(joined.en).toBe('X is empty or not recognizable as a number — recomputation not possible.')
  })
})
