"""Value objects for the timesheet domain.

JalaliDate wraps the conversion between the Gregorian dates stored in the
database and the Jalali (Persian) calendar dates shown to users. The actual
conversion math is delegated to the ``jdatetime`` third-party library, but
callers throughout domain/application code only ever see this thin wrapper
so that swapping the underlying library later stays a one-file change.
"""
from __future__ import annotations

import datetime
from dataclasses import dataclass

import jdatetime


@dataclass(frozen=True)
class JalaliDate:
    year: int
    month: int
    day: int

    @classmethod
    def from_gregorian(cls, g: datetime.date) -> "JalaliDate":
        jd = jdatetime.date.fromgregorian(date=g)
        return cls(jd.year, jd.month, jd.day)

    def to_gregorian(self) -> datetime.date:
        return jdatetime.date(self.year, self.month, self.day).togregorian()

    def __str__(self) -> str:
        return f"{self.year:04d}/{self.month:02d}/{self.day:02d}"


def gregorian_to_jalali_string(g: datetime.date) -> str:
    return str(JalaliDate.from_gregorian(g))


def jalali_month_to_gregorian_range(
    jalali_year: int, jalali_month: int
) -> tuple[datetime.date, datetime.date]:
    """Return (start, end) Gregorian dates (inclusive) for a Jalali month."""
    start = jdatetime.date(jalali_year, jalali_month, 1)
    days_in_month = jdatetime.j_days_in_month[jalali_month - 1]
    # Esfand (month 12) has 29 or 30 days depending on leap year.
    if jalali_month == 12:
        days_in_month = 30 if jdatetime.date(jalali_year, 1, 1).isleap() else 29
    end = jdatetime.date(jalali_year, jalali_month, days_in_month)
    return start.togregorian(), end.togregorian()


_DIGIT_MAP = str.maketrans("۰۱۲۳۴۵۶۷۸۹٠١٢٣٤٥٦٧٨٩", "01234567890123456789")


def normalize_iran_mobile(raw: str | None) -> str | None:
    """Return the canonical 09XXXXXXXXX form, or None if `raw` is not a valid Iranian mobile."""
    if not raw:
        return None
    digits = "".join(ch for ch in str(raw).translate(_DIGIT_MAP) if ch.isdigit() or ch == "+")
    if digits.startswith("+98"):
        digits = "0" + digits[3:]
    elif digits.startswith("0098"):
        digits = "0" + digits[4:]
    elif digits.startswith("98") and len(digits) == 12:
        digits = "0" + digits[2:]
    elif len(digits) == 10 and digits.startswith("9"):
        digits = "0" + digits
    if len(digits) == 11 and digits.startswith("09") and digits.isdigit():
        return digits
    return None


def format_minutes_as_hm(total_minutes: int) -> str:
    """Format a minute count as 'H:MM' (e.g. 90 -> '1:30')."""
    sign = "-" if total_minutes < 0 else ""
    total_minutes = abs(total_minutes)
    hours, minutes = divmod(total_minutes, 60)
    return f"{sign}{hours}:{minutes:02d}"
