"""Astrological module - Thelema Clock and planetary positions.

Calculates current planetary positions with zodiac degrees and symbols.

Usage:
    from clock import ThalemaClock
    from datetime import datetime

    now = datetime.now()
    clock = ThalemaClock(now)
    print(clock)  # Display formatted with degrees and symbols

    # Get individual planet info
    sun_info = clock.get_planet('Sun')
    print(sun_info)  # "☉ 25°♏︎"

    # Custom planet order
    clock.display_format(['Moon', 'Mercury', 'Mars', 'Venus', 'Sun'])
"""

from dataclasses import dataclass
from datetime import datetime
from enum import Enum
from typing import Dict, List, Optional


class Zodiac(Enum):
    """Zodiac signs with degree ranges (0-360°)."""

    ARIES = ("♈", 0, 30)
    TAURUS = ("♉", 30, 60)
    GEMINI = ("♊", 60, 90)
    CANCER = ("♋", 90, 120)
    LEO = ("♌", 120, 150)
    VIRGO = ("♍", 150, 180)
    LIBRA = ("♎", 180, 210)
    SCORPIO = ("♏", 210, 240)
    SAGITTARIUS = ("♐", 240, 270)
    CAPRICORN = ("♑", 270, 300)
    AQUARIUS = ("♒", 300, 330)
    PISCES = ("♓", 330, 360)

    def __init__(self, symbol: str, start_degree: int, end_degree: int):
        """Initialize zodiac with symbol and degree range."""
        self.symbol = symbol
        self.start_degree = start_degree
        self.end_degree = end_degree

    @classmethod
    def from_degree(cls, degree: float) -> "Zodiac":
        """Get zodiac sign from ecliptic degree (0-360)."""
        degree = degree % 360
        for zodiac in cls:
            if zodiac.start_degree <= degree < zodiac.end_degree:
                return zodiac
        return cls.ARIES  # Fallback

    def get_degree_in_sign(self, ecliptic_degree: float) -> float:
        """Get degree within the zodiac sign (0-30)."""
        ecliptic_degree = ecliptic_degree % 360
        return ecliptic_degree - self.start_degree


@dataclass
class PlanetPosition:
    """Represents a planet's position with degree and zodiac."""

    planet_name: str
    planet_symbol: str
    zodiac: Zodiac
    degree_in_sign: float  # 0-30 degrees
    ecliptic_degree: float  # 0-360 degrees

    def __str__(self) -> str:
        """Format as: ☉︎ 25°Scorpio"""
        return f"{self.planet_symbol} {self.degree_in_sign:.0f}° {self.zodiac.name.title()}"

    def __repr__(self) -> str:
        """Detailed representation."""
        return f"PlanetPosition({self.planet_name}, {self})"


class ThalemaClock:
    """Astrological Thelema Clock showing planetary positions with degrees.

    Calculates approximate planetary positions based on date/time for display
    in the format: O 25 Scorpio (Sun at 25 degrees Scorpio).

    Note: Uses simplified ephemeris calculations, not precise astronomical data.
    For precise calculations, use astronomy libraries like Skyfield or PyEphem.
    """

    # Planet symbols (plaintext astronomical notation)
    PLANET_SYMBOLS = {
        "Sun": "☉︎",
        "Moon": "☾︎",
        "Mercury": "☿︎",
        "Venus": "♀︎",
        "Mars": "♂︎",
        "Jupiter": "♃︎",
        "Saturn": "♄︎",
        "Uranus": "♅︎",
        "Neptune": "♆︎",
        "Pluto": "♇︎",
    }

    # Mean orbital periods (days)
    ORBITAL_PERIODS = {
        "Sun": 365.25,
        "Moon": 27.32,  # Sidereal month
        "Mercury": 87.97,
        "Venus": 224.70,
        "Mars": 686.98,
        "Jupiter": 11.86 * 365.25,
        "Saturn": 29.46 * 365.25,
        "Uranus": 84.01 * 365.25,
        "Neptune": 164.79 * 365.25,
        "Pluto": 247.94 * 365.25,
    }

    # Mean longitudes at J2000.0 (Jan 1, 2000, 12:00 UT)
    # Approximate positions in ecliptic longitude
    MEAN_LONGITUDES_J2000 = {
        "Sun": 100.47,
        "Moon": 280.47,  # Mean longitude
        "Mercury": 149.03,
        "Venus": 217.32,
        "Mars": 241.46,
        "Jupiter": 100.47,
        "Saturn": 102.87,
        "Uranus": 33.51,
        "Neptune": 48.02,
        "Pluto": 14.53,
    }

    # Reference epoch (J2000.0)
    J2000_EPOCH = datetime(2000, 1, 1, 12, 0, 0)

    def __init__(self, moment: Optional[datetime] = None):
        """Initialize Thelema Clock for a given moment.

        Args:
            moment: datetime object. Defaults to current time.
        """
        self.moment = moment or datetime.now()
        self.positions: Dict[str, PlanetPosition] = {}
        self._calculate_positions()

    def _calculate_positions(self) -> None:
        """Calculate approximate planetary positions."""
        # Days since J2000.0 epoch
        days_since_j2000 = (self.moment - self.J2000_EPOCH).total_seconds() / 86400

        for planet_name in self.PLANET_SYMBOLS.keys():
            # Get orbital period and mean longitude
            orbital_period = self.ORBITAL_PERIODS.get(planet_name, 365.25)
            mean_longitude_j2000 = self.MEAN_LONGITUDES_J2000.get(planet_name, 0)

            # Calculate mean motion (degrees per day)
            mean_motion = 360 / orbital_period

            # Calculate current ecliptic longitude
            ecliptic_degree = (mean_longitude_j2000 + mean_motion * days_since_j2000) % 360

            # Determine zodiac sign and degree within sign
            zodiac = Zodiac.from_degree(ecliptic_degree)
            degree_in_sign = zodiac.get_degree_in_sign(ecliptic_degree)

            # Create position object
            self.positions[planet_name] = PlanetPosition(
                planet_name=planet_name,
                planet_symbol=self.PLANET_SYMBOLS[planet_name],
                zodiac=zodiac,
                degree_in_sign=degree_in_sign,
                ecliptic_degree=ecliptic_degree,
            )

    def get_planet(self, planet_name: str) -> Optional[PlanetPosition]:
        """Get position for a specific planet.

        Args:
            planet_name: Planet name (e.g., 'Sun', 'Moon', 'Mercury')

        Returns:
            PlanetPosition or None if planet not found
        """
        return self.positions.get(planet_name)

    def get_traditional_planets(self) -> Dict[str, PlanetPosition]:
        """Get the 7 traditional planets (Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn)."""
        traditional = ["Sun", "Moon", "Mercury", "Venus", "Mars", "Jupiter", "Saturn"]
        return {name: self.positions[name] for name in traditional if name in self.positions}

    def display_format(
        self, planet_order: Optional[List[str]] = None, suffix: Optional[str] = None
    ) -> str:
        """Display in custom format.

        Args:
            planet_order: List of planet names to display (e.g., ['Moon', 'Mercury', 'Mars']).
                         Defaults to traditional 7 planets.
            suffix: Optional suffix text (e.g., 'Vx' for Vertex).

        Returns:
            Formatted string like: "☽ 23°♎ : ☿ 2°♐ : ♂ 9°♐ : ♀ 13°♏ : ☉ 25°♏" or with suffix
        """
        if planet_order is None:
            planet_order = ["Sun", "Moon", "Mercury", "Venus", "Mars", "Jupiter", "Saturn"]

        parts = []
        for planet_name in planet_order:
            if planet_name in self.positions:
                parts.append(str(self.positions[planet_name]))
            elif planet_name.lower() == "vertex":
                # Special case for Vertex - could be calculated from Ascendant + Descendant
                parts.append("Vx")

        result = " : ".join(parts)
        if suffix:
            result += f" : {suffix}"
        return result

    def display_compact(self) -> str:
        """Display in compact format.

        Example: ☉︎ 25°Scorpio : ☾︎ 23°Libra : ☿︎ 2°Sagittarius : ♀︎ 13°Scorpio : ♂︎ 9°Sagittarius
        """
        return self.display_format()

    def display_verbose(self) -> str:
        """Display with full planet names and zodiac sign names."""
        lines = []
        for planet_name in ["Sun", "Moon", "Mercury", "Venus", "Mars", "Jupiter", "Saturn"]:
            if planet_name in self.positions:
                pos = self.positions[planet_name]
                lines.append(
                    f"{planet_name:9} {pos.zodiac.name:11} {pos.degree_in_sign:5.1f}° {pos}"
                )
        return "\n".join(lines)

    def __str__(self) -> str:
        """Display as compact thelema clock format."""
        return self.display_compact()

    def __repr__(self) -> str:
        """Detailed representation."""
        timestamp = self.moment.strftime("%Y-%m-%d %H:%M:%S")
        return f"ThalemaClock({timestamp})\n{self.display_verbose()}"
