"""
Common Template Filters for ThrillWiki.

This module provides commonly used template filters for formatting,
text manipulation, and utility operations.

Usage:
    {% load common_filters %}

    {{ timedelta|humanize_timedelta }}
    {{ text|truncate_smart:50 }}
    {{ number|format_number }}
    {{ dict|get_item:"key" }}
"""

from datetime import timedelta

from django import template
from django.template.defaultfilters import stringfilter
from django.utils import timezone

register = template.Library()


# =============================================================================
# Time and Date Filters
# =============================================================================


@register.filter
def humanize_timedelta(value):
    """
    Convert a timedelta or datetime to a human-readable relative time.

    Usage:
        {{ last_updated|humanize_timedelta }}
        Output: "2 hours ago", "3 days ago", "just now"

    Args:
        value: datetime, timedelta, or seconds (int)

    Returns:
        Human-readable string like "2 hours ago"
    """
    if value is None:
        return ""

    # Convert datetime to timedelta from now
    if hasattr(value, "tzinfo"):  # It's a datetime
        now = timezone.now()
        if value > now:
            return "in the future"
        value = now - value

    # Convert seconds to timedelta
    if isinstance(value, int | float):
        value = timedelta(seconds=value)

    if not isinstance(value, timedelta):
        return ""

    seconds = int(value.total_seconds())

    if seconds < 60:
        return "just now"
    elif seconds < 3600:
        minutes = seconds // 60
        return f'{minutes} minute{"s" if minutes != 1 else ""} ago'
    elif seconds < 86400:
        hours = seconds // 3600
        return f'{hours} hour{"s" if hours != 1 else ""} ago'
    elif seconds < 604800:
        days = seconds // 86400
        return f'{days} day{"s" if days != 1 else ""} ago'
    elif seconds < 2592000:
        weeks = seconds // 604800
        return f'{weeks} week{"s" if weeks != 1 else ""} ago'
    elif seconds < 31536000:
        months = seconds // 2592000
        return f'{months} month{"s" if months != 1 else ""} ago'
    else:
        years = seconds // 31536000
        return f'{years} year{"s" if years != 1 else ""} ago'


@register.filter
def time_until(value):
    """
    Convert a future datetime to human-readable time until.

    Usage:
        {{ event_date|time_until }}
        Output: "in 2 days", "in 3 hours"
    """
    if value is None:
        return ""

    if hasattr(value, "tzinfo"):
        now = timezone.now()
        if value <= now:
            return "now"
        diff = value - now
        return humanize_timedelta(diff).replace(" ago", "")

    return ""


# =============================================================================
# Text Manipulation Filters
# =============================================================================


@register.filter
@stringfilter
def truncate_smart(value, max_length=50):
    """
    Truncate text at word boundary, preserving whole words.

    Usage:
        {{ description|truncate_smart:100 }}

    Args:
        value: Text to truncate
        max_length: Maximum length (default: 50)

    Returns:
        Truncated text with "..." if truncated
    """
    max_length = int(max_length)
    if len(value) <= max_length:
        return value

    # Find the last space before max_length
    truncated = value[:max_length]
    last_space = truncated.rfind(" ")

    if last_space > max_length * 0.5:  # Only use word boundary if reasonable
        truncated = truncated[:last_space]

    return truncated.rstrip(".,!?;:") + "..."


@register.filter
@stringfilter
def truncate_middle(value, max_length=50):
    """
    Truncate text in the middle, showing start and end.

    Usage:
        {{ long_filename|truncate_middle:30 }}
        Output: "very_long_fi...le_name.txt"
    """
    max_length = int(max_length)
    if len(value) <= max_length:
        return value

    keep_chars = (max_length - 3) // 2
    return f"{value[:keep_chars]}...{value[-keep_chars:]}"


@register.filter
@stringfilter
def initials(value, max_initials=2):
    """
    Get initials from a name.

    Usage:
        {{ user.full_name|initials }}
        Output: "JD" for "John Doe"
    """
    words = value.split()
    return "".join(word[0].upper() for word in words[:max_initials] if word)


# =============================================================================
# Number Formatting Filters
# =============================================================================


@register.filter
def format_number(value, decimals=0):
    """
    Format number with thousand separators.

    Usage:
        {{ count|format_number }}
        Output: "1,234,567"

        {{ price|format_number:2 }}
        Output: "1,234.56"
    """
    if value is None:
        return ""

    try:
        value = float(value)
        decimals = int(decimals)
        if decimals > 0:
            return f"{value:,.{decimals}f}"
        return f"{int(value):,}"
    except (ValueError, TypeError):
        return value


@register.filter
def format_compact(value):
    """
    Format large numbers compactly (K, M, B).

    Usage:
        {{ view_count|format_compact }}
        Output: "1.2K", "3.4M", "2.1B"
    """
    if value is None:
        return ""

    try:
        value = float(value)
        if value >= 1_000_000_000:
            return f"{value / 1_000_000_000:.1f}B"
        elif value >= 1_000_000:
            return f"{value / 1_000_000:.1f}M"
        elif value >= 1_000:
            return f"{value / 1_000:.1f}K"
        return str(int(value))
    except (ValueError, TypeError):
        return value


@register.filter
def percentage(value, total):
    """
    Calculate percentage of value from total.

    Usage:
        {{ completed|percentage:total }}
        Output: "75%"
    """
    try:
        value = float(value)
        total = float(total)
        if total == 0:
            return "0%"
        return f"{(value / total * 100):.0f}%"
    except (ValueError, TypeError, ZeroDivisionError):
        return "0%"


# =============================================================================
# Dictionary/List Filters
# =============================================================================


@register.filter
def get_item(dictionary, key):
    """
    Get item from dictionary safely in templates.

    Usage:
        {{ my_dict|get_item:"key" }}
        {{ my_dict|get_item:variable_key }}
    """
    if dictionary is None:
        return None
    return dictionary.get(key)


@register.filter
def getlist(querydict, key):
    """
    Get list of values from QueryDict (request.GET/POST).

    Usage:
        {{ request.GET|getlist:"categories" }}

    Args:
        querydict: Django QueryDict (request.GET or request.POST)
        key: Key to retrieve list for

    Returns:
        List of values for the key, or empty list if not found
    """
    if querydict is None:
        return []
    if hasattr(querydict, "getlist"):
        return querydict.getlist(key)
    return []


@register.filter
def get_attr(obj, attr):
    """
    Get attribute from object safely in templates.

    Usage:
        {{ object|get_attr:"field_name" }}
    """
    if obj is None:
        return None
    return getattr(obj, attr, None)


@register.filter
def index(sequence, i):
    """
    Get item by index from list/tuple.

    Usage:
        {{ my_list|index:0 }}
    """
    try:
        return sequence[int(i)]
    except (IndexError, TypeError, ValueError):
        return None


# =============================================================================
# Pluralization Filters
# =============================================================================


@register.filter
def pluralize_custom(count, forms):
    """
    Custom pluralization with specified singular/plural forms.

    Usage:
        {{ count|pluralize_custom:"item,items" }}
        {{ count|pluralize_custom:"person,people" }}
        {{ count|pluralize_custom:"goose,geese" }}

    Args:
        count: Number to check
        forms: Comma-separated "singular,plural" forms
    """
    try:
        count = int(count)
        singular, plural = forms.split(",")
        return singular if count == 1 else plural
    except (ValueError, AttributeError):
        return forms


@register.filter
def count_with_label(count, forms):
    """
    Format count with appropriate label.

    Usage:
        {{ rides|length|count_with_label:"ride,rides" }}
        Output: "1 ride" or "5 rides"
    """
    try:
        count = int(count)
        singular, plural = forms.split(",")
        label = singular if count == 1 else plural
        return f"{count} {label}"
    except (ValueError, AttributeError):
        return str(count)


# =============================================================================
# CSS Class Manipulation
# =============================================================================


@register.filter
def add_class(field, css_class):
    """
    Add CSS class to form field widget.

    Usage:
        {{ form.email|add_class:"form-control" }}
    """
    if hasattr(field, "as_widget"):
        existing = field.field.widget.attrs.get("class", "")
        new_classes = f"{existing} {css_class}".strip()
        return field.as_widget(attrs={"class": new_classes})
    return field


@register.filter
def set_attr(field, attr_value):
    """
    Set attribute on form field widget.

    Usage:
        {{ form.email|set_attr:"placeholder:Enter email" }}
    """
    if hasattr(field, "as_widget"):
        attr, value = attr_value.split(":")
        return field.as_widget(attrs={attr: value})
    return field


# =============================================================================
# Conditional Filters
# =============================================================================


@register.filter
def default_if_none(value, default):
    """
    Return default if value is None (not just falsy).

    Usage:
        {{ value|default_if_none:"N/A" }}
    """
    return default if value is None else value


@register.filter
def yesno_icon(value, icons="fa-check,fa-times"):
    """
    Return icon class based on boolean value.

    Usage:
        {{ is_active|yesno_icon }}
        Output: "fa-check" or "fa-times"

        {{ has_feature|yesno_icon:"fa-star,fa-star-o" }}
    """
    true_icon, false_icon = icons.split(",")
    return true_icon if value else false_icon
