"""
Temporal utilities for Helix.

Provides helpers for parsing temporal expressions and detecting
temporal intent in queries.
"""

from __future__ import annotations

import re
from datetime import datetime, timezone, timedelta
from typing import Any, NamedTuple


class TemporalParams(NamedTuple):
    """Extracted temporal parameters from a query."""
    
    has_temporal_intent: bool
    valid_at: datetime | None  # Point-in-time query
    valid_from: datetime | None  # Range start
    valid_to: datetime | None  # Range end
    temporal_keywords: list[str]  # Detected keywords
    

# Common temporal keywords and patterns
TEMPORAL_KEYWORDS = [
    # Point-in-time
    "when", "at the time", "back then", "at that point",
    "during", "in", "on", "at",
    # Before/after
    "before", "after", "prior to", "following",
    "since", "until", "by",
    # Historical
    "was", "were", "had", "used to", "formerly",
    "originally", "previously", "initially",
    # Current state
    "now", "currently", "today", "presently",
    # Comparative
    "still", "anymore", "changed", "updated",
]

# Date patterns
DATE_PATTERNS = [
    # ISO format: 2024-01-15
    r"\b(\d{4})-(\d{2})-(\d{2})\b",
    # US format: 01/15/2024
    r"\b(\d{1,2})/(\d{1,2})/(\d{4})\b",
    # Written: January 15, 2024
    r"\b(January|February|March|April|May|June|July|August|September|October|November|December)\s+(\d{1,2}),?\s+(\d{4})\b",
    # Year only: in 2024
    r"\bin\s+(\d{4})\b",
    # Relative: 5 days ago
    r"\b(\d+)\s+(days?|weeks?|months?|years?)\s+ago\b",
]

MONTH_MAP = {
    "january": 1, "february": 2, "march": 3, "april": 4,
    "may": 5, "june": 6, "july": 7, "august": 8,
    "september": 9, "october": 10, "november": 11, "december": 12,
}


def is_temporal_query(query: str) -> bool:
    """
    Detect if a query has temporal intent.
    
    Args:
        query: The query string.
        
    Returns:
        True if temporal keywords or patterns detected.
    """
    query_lower = query.lower()
    
    # Check for temporal keywords
    for keyword in TEMPORAL_KEYWORDS:
        if keyword in query_lower:
            return True
    
    # Check for date patterns
    for pattern in DATE_PATTERNS:
        if re.search(pattern, query, re.IGNORECASE):
            return True
    
    return False


def parse_temporal_expression(text: str) -> datetime | None:
    """
    Parse a temporal expression into a datetime.
    
    Args:
        text: Text containing a temporal expression.
        
    Returns:
        Parsed datetime or None if parsing fails.
    """
    text_lower = text.lower().strip()
    now = datetime.now(timezone.utc)
    
    # Try ISO format: 2024-01-15
    match = re.search(r"(\d{4})-(\d{2})-(\d{2})", text)
    if match:
        try:
            return datetime(
                int(match.group(1)),
                int(match.group(2)),
                int(match.group(3)),
                tzinfo=timezone.utc,
            )
        except ValueError:
            pass
    
    # Try US format: 01/15/2024
    match = re.search(r"(\d{1,2})/(\d{1,2})/(\d{4})", text)
    if match:
        try:
            return datetime(
                int(match.group(3)),
                int(match.group(1)),
                int(match.group(2)),
                tzinfo=timezone.utc,
            )
        except ValueError:
            pass
    
    # Try written format: January 15, 2024
    match = re.search(
        r"(January|February|March|April|May|June|July|August|September|October|November|December)\s+(\d{1,2}),?\s+(\d{4})",
        text,
        re.IGNORECASE,
    )
    if match:
        try:
            month = MONTH_MAP[match.group(1).lower()]
            return datetime(
                int(match.group(3)),
                month,
                int(match.group(2)),
                tzinfo=timezone.utc,
            )
        except (ValueError, KeyError):
            pass
    
    # Try year only: in 2024
    match = re.search(r"in\s+(\d{4})", text, re.IGNORECASE)
    if match:
        try:
            return datetime(int(match.group(1)), 1, 1, tzinfo=timezone.utc)
        except ValueError:
            pass
    
    # Try relative: 5 days ago
    match = re.search(r"(\d+)\s+(days?|weeks?|months?|years?)\s+ago", text, re.IGNORECASE)
    if match:
        amount = int(match.group(1))
        unit = match.group(2).lower().rstrip("s")
        
        if unit == "day":
            return now - timedelta(days=amount)
        elif unit == "week":
            return now - timedelta(weeks=amount)
        elif unit == "month":
            return now - timedelta(days=amount * 30)  # Approximate
        elif unit == "year":
            return now - timedelta(days=amount * 365)  # Approximate
    
    # Handle relative keywords
    if "yesterday" in text_lower:
        return now - timedelta(days=1)
    if "today" in text_lower or "now" in text_lower:
        return now
    if "last week" in text_lower:
        return now - timedelta(weeks=1)
    if "last month" in text_lower:
        return now - timedelta(days=30)
    if "last year" in text_lower:
        return now - timedelta(days=365)
    
    return None


def extract_temporal_params(query: str) -> TemporalParams:
    """
    Extract temporal parameters from a query.
    
    Args:
        query: The query string.
        
    Returns:
        TemporalParams with extracted information.
    """
    query_lower = query.lower()
    
    # Detect temporal keywords
    found_keywords = [
        kw for kw in TEMPORAL_KEYWORDS
        if kw in query_lower
    ]
    
    has_temporal = len(found_keywords) > 0
    
    # Try to extract specific dates
    valid_at = parse_temporal_expression(query)
    
    # Detect before/after patterns for ranges
    valid_from = None
    valid_to = None
    
    # "before X" pattern
    match = re.search(r"before\s+(.+?)(?:\s+and|\s+or|\s*$|[.,])", query, re.IGNORECASE)
    if match:
        valid_to = parse_temporal_expression(match.group(1))
        has_temporal = True
    
    # "after X" pattern
    match = re.search(r"after\s+(.+?)(?:\s+and|\s+or|\s*$|[.,])", query, re.IGNORECASE)
    if match:
        valid_from = parse_temporal_expression(match.group(1))
        has_temporal = True
    
    # "between X and Y" pattern
    match = re.search(r"between\s+(.+?)\s+and\s+(.+?)(?:\s*$|[.,])", query, re.IGNORECASE)
    if match:
        valid_from = parse_temporal_expression(match.group(1))
        valid_to = parse_temporal_expression(match.group(2))
        has_temporal = True
    
    return TemporalParams(
        has_temporal_intent=has_temporal or valid_at is not None,
        valid_at=valid_at,
        valid_from=valid_from,
        valid_to=valid_to,
        temporal_keywords=found_keywords,
    )
