/// Byte offset span within the input string.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Span {
    /// Inclusive start byte offset.
    pub start: usize,
    /// Exclusive end byte offset.
    pub end: usize,
}

impl Span {
    /// Create a new span from start (inclusive) to end (exclusive).
    pub fn new(start: usize, end: usize) -> Self {
        Self { start, end }
    }

    /// Length of the span in bytes.
    pub fn len(&self) -> usize {
        self.end - self.start
    }

    /// Whether this span is empty.
    pub fn is_empty(&self) -> bool {
        self.start == self.end
    }
}

/// Arithmetic operator kind.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Operator {
    Plus,
    Minus,
    Star,
    Slash,
    Caret,
    Percent,
}

/// The kind of token produced by the lexer.
#[derive(Debug, Clone, PartialEq)]
pub enum TokenKind {
    /// A numeric literal (integer, decimal, scientific, or SI-scaled).
    Number(f64),
    /// An arithmetic operator.
    Operator(Operator),
    /// An identifier (variable name or function name).
    Identifier(String),
    /// The `=` assignment operator.
    Assign,
    /// A currency symbol ($, €, £, ¥, R$, etc.).
    CurrencySymbol(String),
    /// A unit suffix (kg, g, m, lb, etc.).
    Unit(String),
    /// A `//` comment — text after `//` to end of line.
    Comment(String),
    /// Plain text line with no calculable expression.
    Text(String),
    /// A keyword (in, to, as, of, discount, off, etc.).
    Keyword(String),
    /// A percentage value like `5%` — stores the number before `%`.
    Percent(f64),
    /// Left parenthesis `(`.
    LParen,
    /// Right parenthesis `)`.
    RParen,
}

/// A single token with its kind and byte span in the input.
#[derive(Debug, Clone, PartialEq)]
pub struct Token {
    /// What kind of token this is and its associated value.
    pub kind: TokenKind,
    /// Byte span of this token in the original input.
    pub span: Span,
}

impl Token {
    /// Create a new token.
    pub fn new(kind: TokenKind, span: Span) -> Self {
        Self { kind, span }
    }
}

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

    #[test]
    fn span_new_and_len() {
        let s = Span::new(0, 5);
        assert_eq!(s.start, 0);
        assert_eq!(s.end, 5);
        assert_eq!(s.len(), 5);
        assert!(!s.is_empty());
    }

    #[test]
    fn span_empty() {
        let s = Span::new(3, 3);
        assert!(s.is_empty());
        assert_eq!(s.len(), 0);
    }

    #[test]
    fn token_construction() {
        let tok = Token::new(
            TokenKind::Number(42.0),
            Span::new(0, 2),
        );
        assert_eq!(tok.kind, TokenKind::Number(42.0));
        assert_eq!(tok.span, Span::new(0, 2));
    }

    #[test]
    fn token_operator() {
        let tok = Token::new(
            TokenKind::Operator(Operator::Plus),
            Span::new(0, 1),
        );
        assert_eq!(tok.kind, TokenKind::Operator(Operator::Plus));
    }

    #[test]
    fn token_kinds_equality() {
        assert_eq!(TokenKind::Assign, TokenKind::Assign);
        assert_eq!(TokenKind::LParen, TokenKind::LParen);
        assert_eq!(TokenKind::RParen, TokenKind::RParen);
        assert_ne!(TokenKind::LParen, TokenKind::RParen);
    }
}
