"""
DateTime Utilities

Common datetime manipulation functions.
"""

from datetime import datetime, timezone, timedelta
from typing import Optional, Union
import re


def now_utc() -> datetime:
    """
    Get current datetime in UTC.
    
    Returns:
        Current UTC datetime with timezone info
    """
    return datetime.now(timezone.utc)


def format_datetime(
    dt: datetime,
    format_str: str = "%Y-%m-%d %H:%M:%S",
    timezone_offset: Optional[int] = None,
) -> str:
    """
    Format datetime to string.
    
    Args:
        dt: Datetime object
        format_str: Format string
        timezone_offset: Timezone offset in hours
        
    Returns:
        Formatted datetime string
    """
    if timezone_offset is not None:
        dt = dt + timedelta(hours=timezone_offset)
    return dt.strftime(format_str)


def parse_datetime(
    date_string: str,
    formats: Optional[list[str]] = None,
) -> Optional[datetime]:
    """
    Parse datetime from string.
    
    Args:
        date_string: Datetime string
        formats: List of format strings to try
        
    Returns:
        Parsed datetime or None
    """
    if formats is None:
        formats = [
            "%Y-%m-%dT%H:%M:%S.%fZ",
            "%Y-%m-%dT%H:%M:%SZ",
            "%Y-%m-%dT%H:%M:%S",
            "%Y-%m-%d %H:%M:%S",
            "%Y-%m-%d",
            "%d/%m/%Y",
            "%m/%d/%Y",
        ]
    
    for fmt in formats:
        try:
            return datetime.strptime(date_string, fmt)
        except ValueError:
            continue
    
    return None


def time_ago(dt: datetime, now: Optional[datetime] = None) -> str:
    """
    Get human-readable time difference.
    
    Args:
        dt: Past datetime
        now: Reference datetime (defaults to now)
        
    Returns:
        Human-readable string (e.g., "5 minutes ago")
    """
    if now is None:
        now = now_utc()
    
    # Ensure both are timezone-aware or naive
    if dt.tzinfo is None and now.tzinfo is not None:
        dt = dt.replace(tzinfo=timezone.utc)
    elif dt.tzinfo is not None and now.tzinfo is None:
        now = now.replace(tzinfo=timezone.utc)
    
    diff = now - dt
    seconds = diff.total_seconds()
    
    if seconds < 0:
        return "in the future"
    elif seconds < 60:
        return "just now"
    elif seconds < 3600:
        minutes = int(seconds / 60)
        return f"{minutes} minute{'s' if minutes > 1 else ''} ago"
    elif seconds < 86400:
        hours = int(seconds / 3600)
        return f"{hours} hour{'s' if hours > 1 else ''} ago"
    elif seconds < 2592000:  # 30 days
        days = int(seconds / 86400)
        return f"{days} day{'s' if days > 1 else ''} ago"
    elif seconds < 31536000:  # 365 days
        months = int(seconds / 2592000)
        return f"{months} month{'s' if months > 1 else ''} ago"
    else:
        years = int(seconds / 31536000)
        return f"{years} year{'s' if years > 1 else ''} ago"


def is_expired(
    dt: datetime,
    now: Optional[datetime] = None,
) -> bool:
    """
    Check if datetime has passed.
    
    Args:
        dt: Datetime to check
        now: Reference datetime (defaults to now)
        
    Returns:
        True if expired
    """
    if now is None:
        now = now_utc()
    
    # Ensure timezone awareness
    if dt.tzinfo is None:
        dt = dt.replace(tzinfo=timezone.utc)
    if now.tzinfo is None:
        now = now.replace(tzinfo=timezone.utc)
    
    return now > dt


def add_time(
    dt: datetime,
    days: int = 0,
    hours: int = 0,
    minutes: int = 0,
    seconds: int = 0,
) -> datetime:
    """
    Add time to datetime.
    
    Args:
        dt: Base datetime
        days: Days to add
        hours: Hours to add
        minutes: Minutes to add
        seconds: Seconds to add
        
    Returns:
        New datetime
    """
    return dt + timedelta(
        days=days,
        hours=hours,
        minutes=minutes,
        seconds=seconds,
    )


def start_of_day(dt: datetime) -> datetime:
    """
    Get start of day (midnight).
    
    Args:
        dt: Datetime
        
    Returns:
        Datetime at start of day
    """
    return dt.replace(hour=0, minute=0, second=0, microsecond=0)


def end_of_day(dt: datetime) -> datetime:
    """
    Get end of day (23:59:59.999999).
    
    Args:
        dt: Datetime
        
    Returns:
        Datetime at end of day
    """
    return dt.replace(hour=23, minute=59, second=59, microsecond=999999)
