//! Barcode generation and PDF content stream embedding
//!
//! Supports Code128, Code39, EAN-13, EAN-8, UPC-A. QRCode, DataMatrix, and PDF417
//! are stubbed with "not yet implemented" errors.

use crate::fitz::error::{Error, Result};
use std::fmt::Write;
use thiserror::Error;

// ============================================================================
// BarcodeError
// ============================================================================

/// Custom error type for barcode generation
#[derive(Debug, Error)]
pub enum BarcodeError {
    #[error("Invalid data: {0}")]
    InvalidData(String),

    #[error("Unsupported character: {0}")]
    UnsupportedCharacter(char),

    #[error("Unsupported barcode type: {0}")]
    Unsupported(String),

    #[error("Data length mismatch: expected {expected}, got {actual}")]
    DataLengthMismatch { expected: usize, actual: usize },

    #[error("Empty data")]
    EmptyData,
}

impl From<BarcodeError> for Error {
    fn from(e: BarcodeError) -> Self {
        Error::generic(e.to_string())
    }
}

// ============================================================================
// BarcodeType
// ============================================================================

/// Supported barcode symbologies
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BarcodeType {
    Code128,
    Code39,
    QRCode,
    EAN13,
    EAN8,
    #[allow(non_camel_case_types)]
    UPC_A,
    DataMatrix,
    PDF417,
}

// ============================================================================
// BarcodeConfig
// ============================================================================

/// Configuration for barcode rendering
#[derive(Debug, Clone)]
pub struct BarcodeConfig {
    /// Module width in PDF units (default: 0.5)
    pub width: f32,
    /// Bar height in PDF units (default: 20.0)
    pub height: f32,
    /// Quiet zone (margin) in modules (default: 10)
    pub quiet_zone: u32,
    /// Whether to show human-readable text below barcode (default: true)
    pub show_text: bool,
    /// Font size for text label (default: 8.0)
    pub text_size: f32,
    /// Bar color as RGB 0.0-1.0 (default: black)
    pub bar_color: (f32, f32, f32),
    /// Background color as RGB 0.0-1.0 (default: white)
    pub background_color: (f32, f32, f32),
}

impl Default for BarcodeConfig {
    fn default() -> Self {
        Self {
            width: 0.5,
            height: 20.0,
            quiet_zone: 10,
            show_text: true,
            text_size: 8.0,
            bar_color: (0.0, 0.0, 0.0),
            background_color: (1.0, 1.0, 1.0),
        }
    }
}

// ============================================================================
// Code 128
// ============================================================================

/// Code 128 encoding: 11 modules per symbol, 6 runs (bar-space-bar-space-bar-space)
/// Widths from Wikipedia: value -> [b1,s1,b2,s2,b3,s3] each 1-4
const CODE128_WIDTHS: [[u8; 6]; 107] = [
    [2, 1, 2, 2, 2, 2], // 0
    [2, 2, 2, 1, 2, 2], // 1
    [2, 2, 2, 2, 2, 1], // 2
    [1, 2, 1, 2, 2, 3], // 3
    [1, 2, 1, 3, 2, 2], // 4
    [1, 3, 1, 2, 2, 2], // 5
    [1, 2, 2, 2, 1, 3], // 6
    [1, 2, 2, 3, 1, 2], // 7
    [1, 3, 2, 2, 1, 2], // 8
    [2, 2, 1, 2, 1, 3], // 9
    [2, 2, 1, 3, 1, 2], // 10
    [2, 1, 3, 2, 1, 2], // 11
    [1, 1, 2, 2, 3, 2], // 12
    [1, 2, 2, 1, 3, 2], // 13
    [1, 2, 2, 2, 3, 1], // 14
    [1, 1, 3, 2, 2, 2], // 15
    [1, 2, 3, 1, 2, 2], // 16
    [1, 2, 3, 2, 2, 1], // 17
    [2, 2, 3, 2, 1, 1], // 18
    [2, 2, 1, 1, 3, 2], // 19
    [2, 2, 1, 2, 3, 1], // 20
    [2, 1, 3, 2, 2, 1], // 21
    [2, 2, 3, 1, 2, 1], // 22
    [3, 1, 2, 1, 3, 1], // 23
    [3, 1, 1, 2, 2, 2], // 24
    [3, 2, 1, 1, 2, 2], // 25
    [3, 2, 1, 2, 2, 1], // 26
    [3, 1, 2, 2, 1, 2], // 27
    [3, 2, 2, 1, 1, 2], // 28
    [3, 2, 2, 2, 1, 1], // 29
    [2, 1, 2, 1, 2, 3], // 30
    [2, 1, 2, 3, 2, 1], // 31
    [2, 3, 2, 1, 2, 1], // 32
    [1, 1, 1, 3, 2, 3], // 33
    [1, 3, 1, 1, 2, 3], // 34
    [1, 3, 1, 3, 2, 1], // 35
    [1, 1, 2, 3, 1, 3], // 36
    [1, 3, 2, 1, 1, 3], // 37
    [1, 3, 2, 3, 1, 1], // 38
    [2, 1, 1, 3, 1, 3], // 39
    [2, 3, 1, 1, 1, 3], // 40
    [2, 3, 1, 3, 1, 1], // 41
    [1, 1, 2, 1, 3, 3], // 42
    [1, 1, 2, 3, 3, 1], // 43
    [1, 3, 2, 1, 3, 1], // 44
    [1, 1, 3, 1, 2, 3], // 45
    [1, 1, 3, 3, 2, 1], // 46
    [1, 3, 3, 1, 2, 1], // 47
    [3, 1, 3, 1, 2, 1], // 48
    [2, 1, 1, 3, 3, 1], // 49
    [2, 3, 1, 1, 3, 1], // 50
    [2, 1, 3, 1, 1, 3], // 51
    [2, 1, 3, 3, 1, 1], // 52
    [2, 1, 3, 1, 3, 1], // 53
    [3, 1, 1, 1, 2, 3], // 54
    [3, 1, 1, 3, 2, 1], // 55
    [3, 3, 1, 1, 2, 1], // 56
    [3, 1, 2, 1, 1, 3], // 57
    [3, 1, 2, 3, 1, 1], // 58
    [3, 3, 2, 1, 1, 1], // 59
    [3, 1, 4, 1, 1, 1], // 60
    [2, 2, 1, 4, 1, 1], // 61
    [4, 3, 1, 1, 1, 1], // 62
    [1, 1, 1, 2, 2, 4], // 63
    [1, 1, 1, 4, 2, 2], // 64
    [1, 2, 1, 1, 2, 4], // 65
    [1, 2, 1, 4, 2, 1], // 66
    [1, 4, 1, 1, 2, 2], // 67
    [1, 4, 1, 2, 2, 1], // 68
    [1, 1, 2, 2, 1, 4], // 69
    [1, 1, 2, 4, 1, 2], // 70
    [1, 2, 2, 1, 1, 4], // 71
    [1, 2, 2, 4, 1, 1], // 72
    [1, 4, 2, 1, 1, 2], // 73
    [1, 4, 2, 2, 1, 1], // 74
    [2, 4, 1, 2, 1, 1], // 75
    [2, 2, 1, 1, 1, 4], // 76
    [4, 1, 3, 1, 1, 1], // 77
    [2, 4, 1, 1, 1, 2], // 78
    [1, 3, 4, 1, 1, 1], // 79
    [1, 1, 1, 2, 4, 2], // 80
    [1, 2, 1, 1, 4, 2], // 81
    [1, 2, 1, 2, 4, 1], // 82
    [1, 1, 4, 2, 1, 2], // 83
    [1, 2, 4, 1, 1, 2], // 84
    [1, 2, 4, 2, 1, 1], // 85
    [4, 1, 1, 2, 1, 2], // 86
    [4, 2, 1, 1, 1, 2], // 87
    [4, 2, 1, 2, 1, 1], // 88
    [2, 1, 2, 1, 4, 1], // 89
    [2, 1, 4, 1, 2, 1], // 90
    [4, 1, 2, 1, 2, 1], // 91
    [1, 1, 1, 1, 4, 3], // 92
    [1, 1, 1, 3, 4, 1], // 93
    [1, 3, 1, 1, 4, 1], // 94
    [1, 1, 4, 1, 1, 3], // 95
    [1, 1, 4, 3, 1, 1], // 96
    [4, 1, 1, 1, 1, 3], // 97
    [4, 1, 1, 3, 1, 1], // 98
    [1, 1, 3, 1, 4, 1], // 99
    [1, 1, 4, 1, 3, 1], // 100
    [3, 1, 1, 1, 4, 1], // 101
    [4, 1, 1, 1, 3, 1], // 102
    [2, 1, 1, 4, 1, 2], // 103 Start A
    [2, 1, 1, 2, 1, 4], // 104 Start B
    [2, 1, 1, 2, 3, 2], // 105 Start C
    [2, 3, 3, 1, 1, 1], // 106 Stop
];

fn code128_encode(data: &str) -> std::result::Result<Vec<u8>, BarcodeError> {
    if data.is_empty() {
        return Err(BarcodeError::EmptyData);
    }

    // Auto-select mode: choose start code and encode
    let bytes = data.as_bytes();
    let mut values: Vec<u8> = Vec::new();
    let mut i = 0;

    // Determine optimal start: if 4+ digits at start, use C; else B (or A for control)
    let all_ascii_printable = bytes.iter().all(|&b| b >= 32 && b <= 127);
    let starts_with_digits = bytes.len() >= 4
        && bytes[0].is_ascii_digit()
        && bytes[1].is_ascii_digit()
        && bytes[2].is_ascii_digit()
        && bytes[3].is_ascii_digit();

    let (mut start_code, mut current_set) = if starts_with_digits && all_ascii_printable {
        (105u8, 'C') // Start C
    } else if bytes.iter().any(|&b| b < 32) {
        (103, 'A') // Start A for control chars
    } else {
        (104, 'B') // Start B for general ASCII
    };

    values.push(start_code);

    while i < bytes.len() {
        match current_set {
            'C' => {
                if i + 1 < bytes.len() && bytes[i].is_ascii_digit() && bytes[i + 1].is_ascii_digit()
                {
                    let pair = ((bytes[i] - b'0') * 10 + (bytes[i + 1] - b'0')) as u8;
                    values.push(pair);
                    i += 2;
                } else {
                    current_set = 'B';
                    values.push(100); // Code B
                }
            }
            'B' => {
                if i + 1 < bytes.len()
                    && bytes[i].is_ascii_digit()
                    && bytes[i + 1].is_ascii_digit()
                    && (i + 2 >= bytes.len() || !bytes[i + 2].is_ascii_digit())
                {
                    // Only 2 digits left - stay in B
                    let b = bytes[i];
                    if b >= 32 && b <= 127 {
                        values.push(b - 32);
                        i += 1;
                    } else {
                        return Err(BarcodeError::UnsupportedCharacter(b as char));
                    }
                } else if i + 4 <= bytes.len()
                    && bytes[i].is_ascii_digit()
                    && bytes[i + 1].is_ascii_digit()
                    && bytes[i + 2].is_ascii_digit()
                    && bytes[i + 3].is_ascii_digit()
                {
                    current_set = 'C';
                    values.push(99); // Code C
                } else {
                    let b = bytes[i];
                    if b >= 32 && b <= 127 {
                        values.push(b - 32);
                        i += 1;
                    } else {
                        return Err(BarcodeError::UnsupportedCharacter(b as char));
                    }
                }
            }
            'A' => {
                let b = bytes[i];
                if b <= 95 {
                    values.push(b);
                    i += 1;
                } else {
                    current_set = 'B';
                    values.push(100);
                }
            }
            _ => unreachable!(),
        }
    }

    // Checksum: sum of (position * value) for all symbols, mod 103. Position is 1-indexed.
    let mut sum = 0u32;
    for (i, &v) in values.iter().enumerate() {
        sum += (i + 1) as u32 * v as u32;
    }
    values.push((sum % 103) as u8);

    Ok(values)
}

// ============================================================================
// Code 39
// ============================================================================

/// Code 39: 43 characters. Each character = 9 elements (5 bars, 4 spaces), 3 wide.
/// Encoding: 5 bars + 4 spaces as binary, 1=wide, 0=narrow. Stored as u16: 9 bits.
const CODE39_CHARS: &str = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%*";
// Pattern: 9 bits, 1=wide. Order: bar space bar space bar space bar space bar
// From spec: 0=000110100, 1=100100001, ...
const CODE39_PATTERNS: [u16; 44] = [
    0b000110100, // 0
    0b100100001, // 1
    0b001100001, // 2
    0b101100000, // 3
    0b000101001, // 4
    0b100101000, // 5
    0b001101000, // 6
    0b000100101, // 7
    0b100100100, // 8
    0b001100100, // 9
    0b100001001, // A
    0b001001001, // B
    0b101001000, // C
    0b000011001, // D
    0b100011000, // E
    0b001011000, // F
    0b000001101, // G
    0b100001100, // H
    0b001001100, // I
    0b000011100, // J
    0b100000011, // K
    0b001000011, // L
    0b101000010, // M
    0b000010011, // N
    0b100010010, // O
    0b001010010, // P
    0b000000111, // Q
    0b100000110, // R
    0b001000110, // S
    0b000010110, // T
    0b110000001, // U
    0b011000001, // V
    0b111000000, // W
    0b010010001, // X
    0b110010000, // Y
    0b011010000, // Z
    0b010000101, // -
    0b110000100, // .
    0b010010100, // space
    0b101000100, // $
    0b100100100, // / (same as 8 in some tables - use distinct)
    0b100010100, // +
    0b010100100, // %
    0b010010010, // * (start/stop)
];

fn code39_char_value(c: char) -> Option<usize> {
    CODE39_CHARS.find(c)
}

fn code39_encode(data: &str, with_checksum: bool) -> std::result::Result<Vec<u16>, BarcodeError> {
    if data.is_empty() {
        return Err(BarcodeError::EmptyData);
    }
    for c in data.chars() {
        if c != '*' && code39_char_value(c).is_none() {
            return Err(BarcodeError::UnsupportedCharacter(c));
        }
    }

    let mut patterns = vec![CODE39_PATTERNS[43]]; // *

    let mut sum = 0u32;
    for c in data.chars() {
        if c == '*' {
            continue;
        }
        let idx = code39_char_value(c).ok_or(BarcodeError::UnsupportedCharacter(c))?;
        patterns.push(CODE39_PATTERNS[idx]);
        sum += idx as u32;
    }

    if with_checksum {
        let check_idx = (sum % 43) as usize;
        patterns.push(CODE39_PATTERNS[check_idx]);
    }

    patterns.push(CODE39_PATTERNS[43]); // *

    Ok(patterns)
}

// ============================================================================
// EAN-13 / EAN-8 / UPC-A
// ============================================================================

/// EAN/UPC encoding: 7 modules per digit. 1=bar, 0=space.
/// L (odd left), G (even left), R (right)
const EAN_L: [u8; 10] = [
    0b0001101, 0b0011001, 0b0010011, 0b0111101, 0b0100011, 0b0110001, 0b0101111, 0b0111011,
    0b0110111, 0b0001011,
];
const EAN_G: [u8; 10] = [
    0b0100111, 0b0110011, 0b0011011, 0b0100001, 0b0011101, 0b0111001, 0b0000101, 0b0010001,
    0b0001001, 0b0010111,
];
const EAN_R: [u8; 10] = [
    0b1110010, 0b1100110, 0b1101100, 0b1000010, 0b1011100, 0b1001110, 0b1010000, 0b1000100,
    0b1001000, 0b1110100,
];

/// EAN-13 first digit determines parity for left 6 digits: 0=LLLLLL, 1=LLGLGG, ...
const EAN13_PARITY: [[u8; 6]; 10] = [
    [0, 0, 0, 0, 0, 0], // 0: LLLLLL
    [0, 0, 1, 0, 1, 1], // 1: LLGLGG
    [0, 0, 1, 1, 0, 1], // 2: LLGGLG
    [0, 0, 1, 1, 1, 0], // 3: LLGGGL
    [0, 1, 0, 0, 1, 1], // 4: LGLLGG
    [0, 1, 1, 0, 0, 1], // 5: LGGLLG
    [0, 1, 1, 1, 0, 0], // 6: LGGGLL
    [0, 1, 0, 1, 0, 1], // 7: LGLGLG
    [0, 1, 0, 1, 1, 0], // 8: LGLGGL
    [0, 1, 1, 0, 1, 0], // 9: LGGLGL
];

fn ean_check_digit(digits: &[u8]) -> u8 {
    let n = digits.len();
    let mut sum = 0i32;
    for (i, &d) in digits.iter().enumerate() {
        // Odd positions (1st, 3rd, ...) get weight 3; even get weight 1
        let weight = if (n - i) % 2 == 1 { 3 } else { 1 };
        sum += (d as i32) * weight;
    }
    let r = (10 - (sum % 10)) % 10;
    r as u8
}

fn ean13_encode(digits: &[u8]) -> std::result::Result<Vec<bool>, BarcodeError> {
    if digits.len() != 12 && digits.len() != 13 {
        return Err(BarcodeError::DataLengthMismatch {
            expected: 12,
            actual: digits.len(),
        });
    }
    let mut d = digits.to_vec();
    if d.len() == 12 {
        d.push(ean_check_digit(&d));
    }
    // d[0] determines parity for d[1..7]
    let mut modules = Vec::new();
    // Left guard: 101
    modules.extend([true, false, true]);
    for (i, &digit) in d[1..7].iter().enumerate() {
        let digit = digit as usize;
        let pat = if EAN13_PARITY[d[0] as usize][i] == 0 {
            EAN_L[digit]
        } else {
            EAN_G[digit]
        };
        for b in (0..7).rev() {
            modules.push((pat >> b) & 1 == 1);
        }
    }
    // Center guard: 01010
    modules.extend([false, true, false, true, false]);
    for &digit in &d[7..13] {
        let pat = EAN_R[digit as usize];
        for b in (0..7).rev() {
            modules.push((pat >> b) & 1 == 1);
        }
    }
    // Right guard: 101
    modules.extend([true, false, true]);
    Ok(modules)
}

fn ean8_encode(digits: &[u8]) -> std::result::Result<Vec<bool>, BarcodeError> {
    if digits.len() != 7 && digits.len() != 8 {
        return Err(BarcodeError::DataLengthMismatch {
            expected: 7,
            actual: digits.len(),
        });
    }
    let mut d = digits.to_vec();
    if d.len() == 7 {
        d.push(ean_check_digit(&d));
    }
    let mut modules = Vec::new();
    modules.extend([true, false, true]);
    for &digit in &d[0..4] {
        let pat = EAN_L[digit as usize];
        for b in (0..7).rev() {
            modules.push((pat >> b) & 1 == 1);
        }
    }
    modules.extend([false, true, false, true, false]);
    for &digit in &d[4..8] {
        let pat = EAN_R[digit as usize];
        for b in (0..7).rev() {
            modules.push((pat >> b) & 1 == 1);
        }
    }
    modules.extend([true, false, true]);
    Ok(modules)
}

fn upca_encode(digits: &[u8]) -> std::result::Result<Vec<bool>, BarcodeError> {
    if digits.len() != 11 && digits.len() != 12 {
        return Err(BarcodeError::DataLengthMismatch {
            expected: 11,
            actual: digits.len(),
        });
    }
    let mut d = digits.to_vec();
    if d.len() == 11 {
        d.push(ean_check_digit(&d));
    }
    let mut modules = Vec::new();
    modules.extend([true, false, true]);
    for &digit in &d[0..6] {
        let pat = EAN_L[digit as usize];
        for b in (0..7).rev() {
            modules.push((pat >> b) & 1 == 1);
        }
    }
    modules.extend([false, true, false, true, false]);
    for &digit in &d[6..12] {
        let pat = EAN_R[digit as usize];
        for b in (0..7).rev() {
            modules.push((pat >> b) & 1 == 1);
        }
    }
    modules.extend([true, false, true]);
    Ok(modules)
}

fn parse_digits(s: &str) -> std::result::Result<Vec<u8>, BarcodeError> {
    let mut v = Vec::new();
    for c in s.chars() {
        if c.is_ascii_digit() {
            v.push((c as u8) - b'0');
        } else {
            return Err(BarcodeError::UnsupportedCharacter(c));
        }
    }
    Ok(v)
}

// ============================================================================
// Content stream generation
// ============================================================================

/// Convert a sequence of bar/space widths (modules) to PDF content stream bytes.
/// `modules`: true = bar, false = space. Uses config for dimensions.
fn modules_to_content_stream(
    modules: &[bool],
    config: &BarcodeConfig,
    display_text: Option<&str>,
) -> Vec<u8> {
    let mw = config.width;
    let h = config.height;
    let qz = config.quiet_zone as f32 * mw;
    let (r, g, b) = config.bar_color;
    let (br, bg, bb) = config.background_color;

    let total_width: f32 = modules.iter().map(|_| mw).sum::<f32>();
    let total_with_qz = total_width + 2.0 * qz;

    let mut out = String::new();

    // Save state
    out.push_str("q\n");

    // Background (optional - white by default)
    let _ = write!(
        out,
        "{} {} {} rg\n0 0 {} {} re f\n",
        br,
        bg,
        bb,
        total_with_qz,
        h + (if config.show_text {
            config.text_size + 2.0
        } else {
            0.0
        })
    );

    // Bar color
    let _ = write!(out, "{} {} {} rg\n", r, g, b);

    let mut x = qz;
    for &is_bar in modules {
        if is_bar {
            let _ = write!(out, "{} 0 {} {} re f\n", x, mw, h);
        }
        x += mw;
    }

    if config.show_text {
        if let Some(text) = display_text {
            let escaped: String = text
                .chars()
                .flat_map(|c| match c {
                    '\\' => "\\\\".chars().collect::<Vec<_>>(),
                    '(' => "\\(".chars().collect::<Vec<_>>(),
                    ')' => "\\)".chars().collect::<Vec<_>>(),
                    _ => vec![c],
                })
                .collect();
            let _ = write!(
                out,
                "BT /F1 {} Tf {} {} Td ({}) Tj ET\n",
                config.text_size, qz, -2.0, escaped
            );
        }
    }

    out.push_str("Q\n");
    out.into_bytes()
}

/// Convert Code128 values to module sequence (bars/spaces)
fn code128_values_to_modules(values: &[u8]) -> Vec<bool> {
    let mut modules = Vec::new();
    for &v in values {
        let idx = v as usize;
        if idx >= CODE128_WIDTHS.len() {
            continue;
        }
        let w = &CODE128_WIDTHS[idx];
        for (i, &width) in w.iter().enumerate() {
            let is_bar = i % 2 == 0;
            for _ in 0..width {
                modules.push(is_bar);
            }
        }
    }
    // Stop pattern has extra 2-module bar
    modules.extend([true, true]);
    modules
}

// Code39: each char has 5 bars + 4 spaces. Wide=3 modules, narrow=1.
// Wide = 3 units, narrow = 1. So we output widths: [b,s,b,s,b,s,b,s,b]
fn code39_patterns_to_modules_correct(patterns: &[u16]) -> Vec<bool> {
    let mut modules = Vec::new();
    for &pat in patterns {
        for b in (0..9).rev() {
            let wide = (pat >> b) & 1 == 1;
            let is_bar = b % 2 == 0; // bars at 0,2,4,6,8; spaces at 1,3,5,7
            let count = if wide { 3 } else { 1 };
            for _ in 0..count {
                modules.push(is_bar);
            }
        }
    }
    modules
}

// ============================================================================
// Public API
// ============================================================================

/// Generate PDF content stream bytes that draw the barcode.
///
/// Returns raw content stream operators (e.g. `q`, `re`, `f`, `Q`) that can be
/// embedded in a page's content stream or an XObject.
pub fn generate_barcode_content_stream(
    barcode_type: BarcodeType,
    data: &str,
    config: &BarcodeConfig,
) -> Result<Vec<u8>> {
    let (modules, display_text) = match barcode_type {
        BarcodeType::Code128 => {
            let values = code128_encode(data).map_err(Error::from)?;
            let modules = code128_values_to_modules(&values);
            (modules, data.to_string())
        }
        BarcodeType::Code39 => {
            let patterns = code39_encode(data, false).map_err(Error::from)?;
            let modules = code39_patterns_to_modules_correct(&patterns);
            (modules, data.to_string())
        }
        BarcodeType::EAN13 => {
            let digits = parse_digits(data).map_err(Error::from)?;
            let modules = ean13_encode(&digits).map_err(Error::from)?;
            (modules, data.to_string())
        }
        BarcodeType::EAN8 => {
            let digits = parse_digits(data).map_err(Error::from)?;
            let modules = ean8_encode(&digits).map_err(Error::from)?;
            (modules, data.to_string())
        }
        BarcodeType::UPC_A => {
            let digits = parse_digits(data).map_err(Error::from)?;
            let modules = upca_encode(&digits).map_err(Error::from)?;
            (modules, data.to_string())
        }
        BarcodeType::QRCode => {
            return Err(Error::unsupported("QRCode is not yet implemented"));
        }
        BarcodeType::DataMatrix => {
            return Err(Error::unsupported("DataMatrix is not yet implemented"));
        }
        BarcodeType::PDF417 => {
            return Err(Error::unsupported("PDF417 is not yet implemented"));
        }
    };

    Ok(modules_to_content_stream(
        &modules,
        config,
        Some(&display_text),
    ))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_code128_empty() {
        assert!(matches!(code128_encode(""), Err(BarcodeError::EmptyData)));
    }

    #[test]
    fn test_code128_basic() {
        let r = code128_encode("A");
        assert!(r.is_ok());
        let v = r.unwrap();
        assert!(v.len() >= 2); // start + data + checksum
        assert_eq!(v[0], 104); // Start B
    }

    #[test]
    fn test_code128_numeric() {
        let r = code128_encode("1234");
        assert!(r.is_ok());
        let v = r.unwrap();
        assert_eq!(v[0], 105); // Start C
        assert_eq!(v[1], 12); // 12
        assert_eq!(v[2], 34); // 34
    }

    #[test]
    fn test_code128_checksum() {
        let v = code128_encode("PJJ123C").unwrap();
        // Checksum: sum of (position * value) for all symbols including start, mod 103
        let mut sum = 0u32;
        for (i, &x) in v.iter().enumerate().take(v.len() - 1) {
            sum += (i + 1) as u32 * x as u32;
        }
        assert_eq!(sum % 103, v[v.len() - 1] as u32);
    }

    #[test]
    fn test_code128_mixed() {
        let r = code128_encode("AB12CD");
        assert!(r.is_ok());
    }

    #[test]
    fn test_code39_empty() {
        assert!(matches!(
            code39_encode("", false),
            Err(BarcodeError::EmptyData)
        ));
    }

    #[test]
    fn test_code39_valid_chars() {
        let r = code39_encode("A1B2", false);
        assert!(r.is_ok());
    }

    #[test]
    fn test_code39_invalid_char() {
        assert!(matches!(
            code39_encode("hello", false),
            Err(BarcodeError::UnsupportedCharacter('h'))
        ));
    }

    #[test]
    fn test_code39_checksum() {
        let p = code39_encode("CODE39", true).unwrap();
        let sum: u32 = "CODE39"
            .chars()
            .map(|c| code39_char_value(c).unwrap() as u32)
            .sum();
        let check_idx = (sum % 43) as usize;
        assert_eq!(p[p.len() - 2], CODE39_PATTERNS[check_idx]);
    }

    #[test]
    fn test_ean13_check_digit() {
        let d = parse_digits("590123412345").unwrap();
        let check = ean_check_digit(&d);
        assert_eq!(check, 7); // 5901234123457
    }

    #[test]
    fn test_ean8_check_digit() {
        let d = parse_digits("9638507").unwrap();
        let check = ean_check_digit(&d);
        assert_eq!(check, 4);
    }

    #[test]
    fn test_upca_check_digit() {
        let d = parse_digits("03600029145").unwrap();
        let check = ean_check_digit(&d);
        assert_eq!(check, 2);
    }

    #[test]
    fn test_ean13_encode() {
        let d = parse_digits("590123412345").unwrap();
        let m = ean13_encode(&d).unwrap();
        assert!(!m.is_empty());
        assert!(m.len() > 90); // 3 + 6*7 + 5 + 6*7 + 3 = 95
    }

    #[test]
    fn test_ean8_encode() {
        let d = parse_digits("96385074").unwrap();
        let m = ean8_encode(&d).unwrap();
        assert!(!m.is_empty());
    }

    #[test]
    fn test_upca_encode() {
        let d = parse_digits("036000291452").unwrap();
        let m = upca_encode(&d).unwrap();
        assert!(!m.is_empty());
    }

    #[test]
    fn test_generate_content_stream_code128() {
        let config = BarcodeConfig::default();
        let r = generate_barcode_content_stream(BarcodeType::Code128, "TEST", &config);
        assert!(r.is_ok());
        let bytes = r.unwrap();
        assert!(bytes.contains(&b'q'));
        assert!(bytes.contains(&b'Q'));
        assert!(bytes.windows(2).any(|w| w == b"re"));
    }

    #[test]
    fn test_generate_content_stream_code39() {
        let config = BarcodeConfig::default();
        let r = generate_barcode_content_stream(BarcodeType::Code39, "A1B2", &config);
        assert!(r.is_ok());
    }

    #[test]
    fn test_generate_content_stream_ean13() {
        let config = BarcodeConfig::default();
        let r = generate_barcode_content_stream(BarcodeType::EAN13, "5901234123457", &config);
        assert!(r.is_ok());
    }

    #[test]
    fn test_qrcode_not_implemented() {
        let config = BarcodeConfig::default();
        let r = generate_barcode_content_stream(BarcodeType::QRCode, "test", &config);
        assert!(r.is_err());
    }

    #[test]
    fn test_datamatrix_not_implemented() {
        let config = BarcodeConfig::default();
        let r = generate_barcode_content_stream(BarcodeType::DataMatrix, "test", &config);
        assert!(r.is_err());
    }

    #[test]
    fn test_pdf417_not_implemented() {
        let config = BarcodeConfig::default();
        let r = generate_barcode_content_stream(BarcodeType::PDF417, "test", &config);
        assert!(r.is_err());
    }
}
