from typing import Optional
from datetime import datetime, timedelta
from analogai.foundation.core.value_objects.time import Time
from analogai.foundation.common.utils.time_utils.time_converter import TimeConverter


class RelativeTimeFormatter:
    
    @staticmethod
    def format_for_range(time_obj: Optional[Time], reference_time: Optional[datetime] = None) -> str:
        if not TimeConverter.has_time_data(time_obj):
            return ""
        
        if reference_time is None:
            reference_time = datetime.now()
        
        time_date = TimeConverter.to_datetime(time_obj, reference_time)
        if time_date is None:
            return RelativeTimeFormatter._format_absolute(time_obj)
        
        has_year = time_obj.year is not None
        has_month = time_obj.month is not None
        has_day = time_obj.day is not None
        
        ref_year = reference_time.year
        ref_month = reference_time.month
        
        if has_day:
            time_date_only = time_date.date()
            reference_date_only = reference_time.date()
            
            if time_date_only == reference_date_only:
                return "today"
            
            days_diff = (time_date_only - reference_date_only).days
            
            if days_diff == -1:
                return "yesterday"
            elif days_diff == 1:
                return "tomorrow"
        
        time_year = time_obj.year if has_year else ref_year
        time_month = time_obj.month if has_month else ref_month
        
        if not has_day:
            if time_year == ref_year:
                if has_month and time_month == ref_month:
                    return "this month"
                elif has_month and time_month == ref_month - 1:
                    return "last month"
                elif has_month and time_month == ref_month + 1:
                    return "next month"
            elif time_year == ref_year - 1:
                if has_month:
                    if ref_month == 1 and time_month == 12:
                        return "last month"
                    month_name = RelativeTimeFormatter._get_month_name(time_month)
                    return f"{month_name}, {time_year}"
                return "last year"
            elif time_year == ref_year + 1:
                if has_month:
                    if ref_month == 12 and time_month == 1:
                        return "next month"
                    month_name = RelativeTimeFormatter._get_month_name(time_month)
                    return f"{month_name}, {time_year}"
                return "next year"
        
        if time_year == ref_year:
            if has_month and time_month == ref_month:
                if has_day:
                    return RelativeTimeFormatter._format_absolute(time_obj)
                return "this month"
            elif has_month and time_month == ref_month - 1:
                return "last month"
            elif has_month and time_month == ref_month + 1:
                return "next month"
            elif has_month:
                month_name = RelativeTimeFormatter._get_month_name(time_month)
                if has_day:
                    return f"{month_name} {time_obj.day}"
                return month_name
        elif time_year == ref_year - 1:
            if has_month:
                if ref_month == 1 and time_month == 12:
                    return "last month"
                if time_month == ref_month:
                    return "last year this month"
                month_name = RelativeTimeFormatter._get_month_name(time_month)
                if has_day:
                    return f"{month_name} {time_obj.day}, {time_year}"
                return f"{month_name}, {time_year}"
            return "last year"
        elif time_year == ref_year + 1:
            if has_month:
                if ref_month == 12 and time_month == 1:
                    return "next month"
                if time_month == ref_month:
                    return "next year this month"
                month_name = RelativeTimeFormatter._get_month_name(time_month)
                if has_day:
                    return f"{month_name} {time_obj.day}, {time_year}"
                return f"{month_name}, {time_year}"
            return "next year"
        
        return RelativeTimeFormatter._format_absolute(time_obj)
    
    @staticmethod
    def _format_absolute(time_obj: Time) -> str:
        date_parts = []

        has_year = time_obj.year is not None
        has_month = time_obj.month is not None
        has_day = time_obj.day is not None

        month_name = None
        if has_month:
            month_name = RelativeTimeFormatter._get_month_name(time_obj.month)

        if has_month and has_day:
            date_parts.append(f"{month_name} {time_obj.day}")
            if has_year:
                date_parts[-1] = f"{date_parts[-1]}, {time_obj.year}"
        elif has_month and has_year:
            date_parts.append(f"{month_name} {time_obj.year}")
        elif has_month:
            date_parts.append(f"{month_name}")
        elif has_day and has_year:
            date_parts.append(f"{time_obj.day}, {time_obj.year}")
        elif has_day:
            date_parts.append(str(time_obj.day))
        elif has_year:
            date_parts.append(str(time_obj.year))

        time_part = ""
        if time_obj.hour is not None:
            if time_obj.minute is not None:
                time_part = f"{time_obj.hour:02d}:{time_obj.minute:02d}"
            else:
                time_part = f"{time_obj.hour:02d}"
        elif time_obj.minute is not None:
            time_part = f":{time_obj.minute:02d}"

        if time_part:
            if date_parts:
                return f"{date_parts[0]} {time_part}"
            return time_part

        return date_parts[0] if date_parts else ""
    
    @staticmethod
    def _get_month_name(month: int) -> str:
        months = [
            "January", "February", "March", "April", "May", "June",
            "July", "August", "September", "October", "November", "December"
        ]
        if 1 <= month <= 12:
            return months[month - 1]
        return str(month)
