"""
Main time expression parser class that integrates all components.
"""

import datetime
from typing import Optional

from .lexer import Lexer
from .parser import Parser
from .semantic import SemanticAnalyzer
from .ptime_ast import TimeExpressionNode
from .err import TokenUnhandledException


class TimeExpression:
    """Main class for parsing time expressions."""
    
    def __init__(self, text: str, now: Optional[datetime.datetime] = None):
        self.text = text.strip()
        self.now = now or datetime.datetime.now()
        
        if not self.text:
            raise TokenUnhandledException("Empty input")
        
        # Initialize components
        self.lexer = Lexer(self.text, self.now)
        self.parser = Parser(self.text, self.now)
        self.semantic_analyzer = SemanticAnalyzer(self.now)
        
        # Parse the expression
        self.ast = self._parse()
    
    def _parse(self) -> TimeExpressionNode:
        """Parse the time expression and return the AST."""
        try:
            return self.parser.parse()
        except Exception as e:
            raise TokenUnhandledException(f"Failed to parse '{self.text}': {str(e)}")
    
    def evaluate(self) -> datetime.datetime:
        """Evaluate the time expression and return the datetime."""
        try:
            return self.semantic_analyzer.evaluate(self.ast)
        except Exception as e:
            raise TokenUnhandledException(f"Failed to evaluate '{self.text}': {str(e)}")
    
    @classmethod
    def parse(cls, text: str, now: Optional[datetime.datetime] = None) -> datetime.datetime:
        """
        Parse a time expression and return a datetime object.
        
        Args:
            text: The time expression to parse
            now: The reference time (defaults to current time)
            
        Returns:
            A datetime object representing the parsed time
            
        Raises:
            TokenUnhandledException: If the input cannot be parsed
        """
        expression = cls(text, now)
        return expression.evaluate()