"""
Absfuyu: Fun
------------
Some fun or weird stuff

Version: 6.8.0
Date updated: 22/05/2026 (dd/mm/yyyy)
"""

# Module level
# ---------------------------------------------------------------------------
__all__ = ["zodiac_sign", "happy_new_year", "human_year_to_dog_year"]


# Library
# ---------------------------------------------------------------------------
from datetime import date
from typing import Final

from absfuyu.core import deprecated, versionadded, versionchanged
from absfuyu.logger import logger
from absfuyu.tools.shutdownizer import ShutDownizer
from absfuyu.util.lunar import LunarCalendar


# Function
# ---------------------------------------------------------------------------
# @versionchanged("5.0.0", reason="Updated functionality")
# def zodiac_sign(day: int, month: int, zodiac13: bool = False) -> str:
#     """
#     Calculate zodiac sign

#     Parameters
#     ----------
#     day : int
#         Day (in range [1, 31])

#     month : int
#         Month (in range [1, 12])

#     zodiac13 : bool
#         13 zodiacs mode (Default: ``False``)

#     Returns
#     -------
#     str
#         Zodiac sign
#     """

#     # Condition check
#     if not 1 <= month <= 12 or not 1 <= day <= 31:
#         raise ValueError("Value out of range")

#     if zodiac13:
#         zodiac_dates = {
#             "Capricorn": ((1, 21), (2, 16)),
#             "Aquarius": ((2, 17), (3, 11)),
#             "Pisces": ((3, 12), (4, 18)),
#             "Aries": ((4, 19), (5, 13)),
#             "Taurus": ((5, 14), (6, 21)),
#             "Gemini": ((6, 22), (7, 20)),
#             "Cancer": ((7, 21), (8, 10)),
#             "Leo": ((8, 11), (9, 16)),
#             "Virgo": ((9, 17), (10, 30)),
#             "Libra": ((10, 31), (11, 23)),
#             "Scorpio": ((11, 24), (11, 29)),
#             "Ophiuchus": ((11, 30), (12, 17)),
#             "Sagittarius": ((12, 18), (1, 20)),
#         }
#     else:
#         zodiac_dates = {
#             "Capricorn (E)": ((12, 22), (1, 19)),
#             "Aquarius (A)": ((1, 20), (2, 18)),
#             "Pisces (W)": ((2, 19), (3, 20)),
#             "Aries (F)": ((3, 21), (4, 19)),
#             "Taurus (E)": ((4, 20), (5, 20)),
#             "Gemini (A)": ((5, 21), (6, 20)),
#             "Cancer (W)": ((6, 21), (7, 22)),
#             "Leo (F)": ((7, 23), (8, 22)),
#             "Virgo (E)": ((8, 23), (9, 22)),
#             "Libra (A)": ((9, 23), (10, 22)),
#             "Scorpio (W)": ((10, 23), (11, 21)),
#             "Sagittarius (F)": ((11, 22), (12, 21)),
#         }

#     result = ""
#     for sign, (start_date, end_date) in zodiac_dates.items():
#         start_month, start_day = start_date
#         end_month, end_day = end_date

#         if (month == start_month and day >= start_day) or (
#             month == end_month and day <= end_day
#         ):
#             result = sign
#             break
#     return result

def zodiac_sign(day: int, month: int, zodiac13: bool = False) -> str:
    """
    Return the zodiac sign for a given date.

    Parameters
    ----------
    day : int
        Day of month.

    month : int
        Month number in range [1, 12].

    zodiac13 : bool, default=False
        Whether to use the 13-sign zodiac system.

    Returns
    -------
    str
        Zodiac sign name.

    Raises
    ------
    ValueError
        If the date is invalid.
    """

    ZODIAC_12: Final[tuple[tuple[str, tuple[int, int], tuple[int, int]], ...]] = (
        ("Capricorn (E)", (12, 22), (1, 19)),
        ("Aquarius (A)", (1, 20), (2, 18)),
        ("Pisces (W)", (2, 19), (3, 20)),
        ("Aries (F)", (3, 21), (4, 19)),
        ("Taurus (E)", (4, 20), (5, 20)),
        ("Gemini (A)", (5, 21), (6, 20)),
        ("Cancer (W)", (6, 21), (7, 22)),
        ("Leo (F)", (7, 23), (8, 22)),
        ("Virgo (E)", (8, 23), (9, 22)),
        ("Libra (A)", (9, 23), (10, 22)),
        ("Scorpio (W)", (10, 23), (11, 21)),
        ("Sagittarius (F)", (11, 22), (12, 21)),
    )

    ZODIAC_13: Final[tuple[tuple[str, tuple[int, int], tuple[int, int]], ...]] = (
        ("Capricorn", (1, 21), (2, 16)),
        ("Aquarius", (2, 17), (3, 11)),
        ("Pisces", (3, 12), (4, 18)),
        ("Aries", (4, 19), (5, 13)),
        ("Taurus", (5, 14), (6, 21)),
        ("Gemini", (6, 22), (7, 20)),
        ("Cancer", (7, 21), (8, 10)),
        ("Leo", (8, 11), (9, 16)),
        ("Virgo", (9, 17), (10, 30)),
        ("Libra", (10, 31), (11, 23)),
        ("Scorpio", (11, 24), (11, 29)),
        ("Ophiuchus", (11, 30), (12, 17)),
        ("Sagittarius", (12, 18), (1, 20)),
    )

    def _in_range(
        target: tuple[int, int],
        start: tuple[int, int],
        end: tuple[int, int],
    ) -> bool:
        """
        Check whether a month/day pair is within a zodiac date range.
        """

        if start <= end:
            return start <= target <= end

        # Cross-year range (e.g. Dec 22 -> Jan 19)
        return target >= start or target <= end

    # Validate real calendar date
    try:
        date(2000, month, day)
    except ValueError as exc:
        raise ValueError("Invalid date") from exc

    target = (month, day)
    zodiac_data = ZODIAC_13 if zodiac13 else ZODIAC_12

    for sign, start, end in zodiac_data:
        if _in_range(target, start, end):
            return sign

    raise RuntimeError("No zodiac sign matched")


# For new year only
def happy_new_year(forced: bool = False, include_lunar: bool = False) -> None:
    """
    Only occurs on 01/01 every year

    Parameters
    ----------
    forced : bool
        Shutdown ASAP (Default: ``False``)

    include_lunar : bool
        Include Lunar New Year (Default: ``False``)
    """

    if forced:
        return ShutDownizer().shutdown()

    today = date.today()
    m = today.month
    d = today.day
    solar_new_year = m == 1 and d == 1
    logger.debug(f"Solar: {today}")

    if include_lunar:
        lunar = LunarCalendar.now().date
        lunar_new_year = lunar.month == 1 and lunar.day == 1
        logger.debug(f"Lunar: {lunar}")
    else:
        lunar_new_year = False

    if solar_new_year or lunar_new_year:
        print("Happy New Year! You should take rest now.")
        return ShutDownizer().shutdown()
    else:
        raise SystemExit("The time has not come yet")


@versionadded("5.0.0")
def human_year_to_dog_year(
    human_year: int | float, is_small: bool = True
) -> int | float:
    """
    Convert human's year to dog's year

    General Guidelines:
    1. First Year: The first year of a dog's life
    is roughly equal to 15 human years.
    2. Second Year: The second year adds about 9 human years,
    making a 2-year-old dog equivalent to about 24 human years.
    3. Subsequent Years: After the second year,
    each additional yeartypically equals about 4-5 human years,
    depending on the dog's size and breed. (Small-4, Large-5)

    Parameters
    ----------
    human_year : int | float
        Dog's age in human year

    is_small : bool, optional
        Is the dog small or not, by default ``True``

    Returns
    -------
    int | float
        Dog's year/age

    Raises
    ------
    ValueError
        When ``human_year`` < 0
    """

    if human_year < 0:
        raise ValueError("Value must be positive")
    if human_year <= 1:
        return human_year * 15
    elif human_year <= 2:
        return 15 + (human_year - 1) * 9
    else:
        if is_small:
            return 24 + (human_year - 2) * 4
        else:
            return 24 + (human_year - 2) * 5
