"""
Thelema Calendar calculation function.
Converts Gregorian calendar to Thelema Calendar (Year of the Beast).

References:
- Year 1 of Thelema = 1904 CE (Spring Equinox)
- 22-year cycles (The Great Year)
- Weekday planetary rulers
"""

from datetime import datetime, timedelta, timezone
from typing import Dict, Optional


# Planetary symbols for weekdays (Sun=0, Mon=1, ..., Sat=6)
WEEKDAY_PLANETS = ["☉︎", "☽︎", "♂︎", "☿︎", "♃︎", "♀︎", "♄︎"]

# Luminary symbols
LUMINARY_SYMBOLS = {
    "Moon": "☾︎",
    "Mercury": "☿︎",
    "Mars": "♂︎",
    "Venus": "♀︎",
    "Sun": "☉︎",
}

# Luminary order
LUMINARIES = ["Moon", "Mercury", "Mars", "Venus", "Sun"]


def to_roman(num: int) -> str:
    """Convert an integer to Roman numerals.
    
    Args:
        num: Integer to convert (1-3999)
        
    Returns:
        Roman numeral string
    """
    val = [
        1000, 900, 500, 400,
        100, 90, 50, 40,
        10, 9, 5, 4,
        1
    ]
    syms = [
        "M", "CM", "D", "CD",
        "C", "XC", "L", "XL",
        "X", "IX", "V", "IV",
        "I"
    ]
    roman_num = ''
    i = 0
    while num > 0:
        for _ in range(num // val[i]):
            roman_num += syms[i]
            num -= val[i]
        i += 1
    return roman_num


def _apply_gmt_offset(moment: datetime, gmt_offset: str) -> datetime:
    """Apply GMT offset to a datetime object.
    
    Args:
        moment: datetime object to convert
        gmt_offset: Offset as string (e.g., "+3:00", "-8:00", "+5:30")
        
    Returns:
        datetime object adjusted to the specified timezone
        
    Example:
        >>> utc_time = datetime(2025, 11, 16, 12, 0, 0)
        >>> pst_time = _apply_gmt_offset(utc_time, "-8:00")
        >>> pst_time.hour
        4  # 12:00 UTC - 8 hours = 04:00 PST
    """
    # Parse GMT offset (e.g., "+3:00" or "-8:00")
    gmt_offset = gmt_offset.strip()
    
    # Handle format like "+3:00", "-8:00", "+5:30"
    if ":" in gmt_offset:
        sign = 1 if gmt_offset[0] == "+" else -1
        parts = gmt_offset[1:].split(":")
        hours = int(parts[0])
        minutes = int(parts[1]) if len(parts) > 1 else 0
        offset_seconds = sign * (hours * 3600 + minutes * 60)
    else:
        # Handle format like "+3" or "-8"
        sign = 1 if gmt_offset[0] == "+" else -1
        hours = int(gmt_offset[1:])
        offset_seconds = sign * hours * 3600
    
    # Create timezone with the offset
    tz = timezone(timedelta(seconds=offset_seconds))
    
    # Convert the naive datetime to aware datetime in UTC, then convert to target timezone
    utc_dt = moment.replace(tzinfo=timezone.utc)
    local_dt = utc_dt.astimezone(tz)
    
    # Return as naive datetime (without timezone info) for calculation
    return local_dt.replace(tzinfo=None)


def get_astro_data() -> Dict[str, str]:
    """Get current astrological data for luminaries.
    
    This is a placeholder that returns mock data.
    In a real implementation, you would scrape astro.com or use an astrology library.
    
    Returns:
        Dictionary mapping luminary names to their astrological positions
        Format: "26°Taurus", "19°Sagittarius", etc.
    """
    # Mock data - replace with actual astrological calculations
    astro_data = {
        "Moon": "24°Sagittarius",
        "Mercury": "7°Aquarius",
        "Mars": "4°Virgo",
        "Venus": "30°Scorpio",
        "Sun": "26°Taurus",
    }
    return astro_data


def thelema_calendar(moment: Optional[datetime] = None, gmt_offset: Optional[str] = None) -> str:
    """Calculate and return the Thelema Calendar date.
    
    The Thelema Calendar counts from the Spring Equinox of 1904 CE (Year 1 of the Beast).
    It divides time into 22-year cycles and displays planetary correspondences.
    
    Args:
        moment: datetime object (defaults to current date/time)
        gmt_offset: GMT offset as string (e.g., "+3:00", "-8:00", "+5:30")
                   If provided, converts the given moment to this timezone before calculating.
                   Example: "+3:00" for UTC+3, "-8:00" for PST (UTC-8), "+5:30" for IST
        
    Returns:
        Formatted Thelema Calendar string with format:
        "[Luminary positions] : [Weekday planet] : [Epoch][Year]"
        
    Example:
        >>> # Current time in UTC
        >>> Tarot.thelema_calendar()
        
        >>> # Specific time in PST (UTC-8)
        >>> from datetime import datetime
        >>> moment = datetime(2025, 11, 16, 14, 30, 0)
        >>> Tarot.thelema_calendar(moment, "-8:00")
        
        >>> # IST (UTC+5:30)
        >>> Tarot.thelema_calendar(gmt_offset="+5:30")
    """
    if moment is None:
        moment = datetime.now()
    
    # Apply timezone offset if provided
    if gmt_offset is not None:
        moment = _apply_gmt_offset(moment, gmt_offset)
    
    today = moment.date()
    current_year = today.year
    
    # Calculate year difference from Thelema epoch (1904)
    year_diff = current_year - 1904
    
    # Check if we've passed the spring equinox (March 22)
    # If yes, increment the year count
    equinox_passed = (today.month > 3) or (today.month == 3 and today.day >= 22)
    if equinox_passed:
        year_diff += 1
    
    # Calculate epoch (22-year cycles)
    epoch_count = year_diff // 22
    epoch_roman = to_roman(epoch_count)
    
    # Calculate year within current 22-year cycle
    thelema_year = (current_year - 12) % 22
    thelema_year_roman = to_roman(thelema_year).lower()
    
    # Get weekday (0=Monday, 6=Sunday in Python; we want 0=Sunday for alignment)
    # Python's weekday(): Monday=0, Sunday=6
    # We want: Sunday=0, Monday=1, ..., Saturday=6
    python_weekday = today.weekday()  # 0=Mon, 6=Sun
    weekday_index = (python_weekday + 1) % 7  # Convert to Sun=0, Mon=1, etc.
    
    # Determine weekday based on UTC hour (or provided moment hour)
    utc_hour = moment.hour  # Note: this is local hour, not UTC in this simplified version
    if utc_hour >= 0 and utc_hour < 14:
        day_weekday = weekday_index % 7
    else:
        day_weekday = (weekday_index + 1) % 7
    
    weekday_symbol = WEEKDAY_PLANETS[day_weekday]
    
    # Get astrological data
    astro_data = get_astro_data()
    
    # Build luminary string
    luminary_strings = []
    for luminary in LUMINARIES:
        symbol = LUMINARY_SYMBOLS[luminary]
        position = astro_data.get(luminary, "unknown")
        luminary_strings.append(f"{symbol} {position}")
    
    # Combine all cycles
    cycles = [
        " : ".join(luminary_strings),
        weekday_symbol,
        f"{epoch_roman}{thelema_year_roman}",
    ]
    
    return " : ".join(cycles)


