"""
Template tags for FSM (Finite State Machine) operations.

This module provides template tags and filters for working with FSM-enabled
models in Django templates, including transition buttons, status displays,
and permission checks.

Usage:
    {% load fsm_tags %}

    {# Get available transitions for an object #}
    {% get_available_transitions submission request.user as transitions %}

    {# Check if a specific transition is allowed #}
    {% can_transition submission 'approve' request.user as can_approve %}

    {# Get the current state value #}
    {{ submission|get_state_value }}

    {# Get the current state display #}
    {{ submission|get_state_display }}

    {# Render a transition button #}
    {% transition_button submission 'approve' request.user %}
"""

from typing import Any

from django import template
from django.urls import NoReverseMatch, reverse
from django_fsm import can_proceed

from apps.core.views.views import get_transition_metadata

register = template.Library()


# =============================================================================
# Filters for State Machine Properties
# =============================================================================


@register.filter
def get_state_value(obj) -> str | None:
    """
    Get the current state value of an FSM-enabled object.

    Usage:
        {{ object|get_state_value }}

    Args:
        obj: An FSM-enabled model instance

    Returns:
        The current state value or None
    """
    if hasattr(obj, "get_state_value"):
        return obj.get_state_value()
    if hasattr(obj, "state_field_name"):
        return getattr(obj, obj.state_field_name, None)
    # Try common field names
    for field in ["status", "state"]:
        if hasattr(obj, field):
            return getattr(obj, field, None)
    return None


@register.filter
def get_state_display(obj) -> str:
    """
    Get the display value for the current state.

    Usage:
        {{ object|get_state_display }}

    Args:
        obj: An FSM-enabled model instance

    Returns:
        The human-readable state display value
    """
    if hasattr(obj, "get_state_display_value"):
        return obj.get_state_display_value()
    if hasattr(obj, "state_field_name"):
        field_name = obj.state_field_name
        getter = getattr(obj, f"get_{field_name}_display", None)
        if callable(getter):
            return getter()
    # Try common field names
    for field in ["status", "state"]:
        getter = getattr(obj, f"get_{field}_display", None)
        if callable(getter):
            return getter()
    return str(get_state_value(obj) or "")


@register.filter
def get_state_choice(obj):
    """
    Get the RichChoice object for the current state.

    Usage:
        {% with choice=object|get_state_choice %}
            {{ choice.metadata.icon }}
        {% endwith %}

    Args:
        obj: An FSM-enabled model instance

    Returns:
        The RichChoice object or None
    """
    if hasattr(obj, "get_state_choice"):
        return obj.get_state_choice()
    return None


@register.filter
def app_label(obj) -> str:
    """
    Get the app label of a model instance.

    Usage:
        {{ object|app_label }}

    Args:
        obj: A Django model instance

    Returns:
        The app label string
    """
    return obj._meta.app_label


@register.filter
def model_name(obj) -> str:
    """
    Get the model name (lowercase) of a model instance.

    Usage:
        {{ object|model_name }}

    Args:
        obj: A Django model instance

    Returns:
        The model name in lowercase
    """
    return obj._meta.model_name


@register.filter
def default_target_id(obj) -> str:
    """
    Get the default HTMX target ID for an object.

    Usage:
        {{ object|default_target_id }}

    Args:
        obj: A Django model instance

    Returns:
        The target ID string (e.g., "editsubmission-123")
    """
    return f"{obj._meta.model_name}-{obj.pk}"


# =============================================================================
# Assignment Tags for Transition Operations
# =============================================================================


@register.simple_tag
def get_available_transitions(obj, user) -> list[dict[str, Any]]:
    """
    Get all available transitions for an object that the user can execute.

    This tag checks each transition method on the object and returns metadata
    for transitions that can proceed for the given user.

    Usage:
        {% get_available_transitions submission request.user as transitions %}
        {% for transition in transitions %}
            <button>{{ transition.label }}</button>
        {% endfor %}

    Args:
        obj: An FSM-enabled model instance
        user: The user to check permissions for

    Returns:
        List of transition metadata dictionaries with keys:
        - name: The method name
        - label: Human-readable label
        - icon: Font Awesome icon name
        - style: Button style (green, red, yellow, blue, gray)
        - requires_confirm: Whether to show confirmation dialog
        - confirm_message: Confirmation message to display
    """
    transitions = []

    if not obj or not user:
        return transitions

    # Get list of available transitions
    available_transition_names = []

    if hasattr(obj, "get_available_user_transitions"):
        # Use the helper method if available
        return obj.get_available_user_transitions(user)

    if hasattr(obj, "get_available_transitions"):
        available_transition_names = list(obj.get_available_transitions())
    else:
        # Fallback: look for transition methods by convention
        for attr_name in dir(obj):
            if attr_name.startswith("transition_to_") or attr_name in [
                "approve",
                "reject",
                "escalate",
                "complete",
                "cancel",
            ]:
                method = getattr(obj, attr_name, None)
                if callable(method) and hasattr(method, "_django_fsm"):
                    available_transition_names.append(attr_name)

    # Filter transitions by user permission
    for transition_name in available_transition_names:
        method = getattr(obj, transition_name, None)
        if method and callable(method):
            try:
                if can_proceed(method, user):
                    metadata = get_transition_metadata(transition_name)
                    transitions.append(
                        {
                            "name": transition_name,
                            "label": _format_transition_label(transition_name),
                            "icon": metadata.get("icon", "arrow-right"),
                            "style": metadata.get("style", "gray"),
                            "requires_confirm": metadata.get("requires_confirm", False),
                            "confirm_message": metadata.get("confirm_message", "Are you sure?"),
                        }
                    )
            except Exception:
                # Skip transitions that raise errors during can_proceed check
                pass

    return transitions


@register.simple_tag
def can_transition(obj, transition_name: str, user) -> bool:
    """
    Check if a specific transition can be executed by the user.

    Usage:
        {% can_transition submission 'approve' request.user as can_approve %}
        {% if can_approve %}
            <button>Approve</button>
        {% endif %}

    Args:
        obj: An FSM-enabled model instance
        transition_name: The name of the transition method
        user: The user to check permissions for

    Returns:
        True if the transition can proceed, False otherwise
    """
    if not obj or not user or not transition_name:
        return False

    method = getattr(obj, transition_name, None)
    if not method or not callable(method):
        return False

    try:
        return can_proceed(method, user)
    except Exception:
        return False


@register.simple_tag
def get_transition_url(obj, transition_name: str) -> str:
    """
    Get the URL for executing a transition on an object.

    Usage:
        {% get_transition_url submission 'approve' as approve_url %}

    Args:
        obj: An FSM-enabled model instance
        transition_name: The name of the transition method

    Returns:
        The URL string for the transition endpoint
    """
    try:
        return reverse(
            "core:fsm_transition",
            kwargs={
                "app_label": obj._meta.app_label,
                "model_name": obj._meta.model_name,
                "pk": obj.pk,
                "transition_name": transition_name,
            },
        )
    except NoReverseMatch:
        return ""


# =============================================================================
# Inclusion Tags for Rendering Components
# =============================================================================


@register.inclusion_tag("htmx/state_actions.html", takes_context=True)
def render_state_actions(context, obj, user=None, **kwargs):
    """
    Render the state action buttons for an FSM-enabled object.

    Usage:
        {% render_state_actions submission request.user %}
        {% render_state_actions submission request.user button_size='sm' %}

    Args:
        context: Template context
        obj: An FSM-enabled model instance
        user: The user to check permissions for (defaults to request.user)
        **kwargs: Additional template context variables

    Returns:
        Context for the state_actions.html template
    """
    if user is None:
        user = context.get("request", {}).user if "request" in context else None

    return {
        "object": obj,
        "user": user,
        "request": context.get("request"),
        **kwargs,
    }


@register.inclusion_tag("htmx/status_with_actions.html", takes_context=True)
def render_status_with_actions(context, obj, user=None, **kwargs):
    """
    Render the status badge with action buttons for an FSM-enabled object.

    Usage:
        {% render_status_with_actions submission request.user %}
        {% render_status_with_actions submission request.user dropdown_actions=True %}

    Args:
        context: Template context
        obj: An FSM-enabled model instance
        user: The user to check permissions for (defaults to request.user)
        **kwargs: Additional template context variables

    Returns:
        Context for the status_with_actions.html template
    """
    if user is None:
        user = context.get("request", {}).user if "request" in context else None

    return {
        "object": obj,
        "user": user,
        "request": context.get("request"),
        **kwargs,
    }


# =============================================================================
# Helper Functions
# =============================================================================


def _format_transition_label(transition_name: str) -> str:
    """
    Format a transition method name into a human-readable label.

    Examples:
        'transition_to_approved' -> 'Approve'
        'approve' -> 'Approve'
        'reject_submission' -> 'Reject Submission'

    Args:
        transition_name: The transition method name

    Returns:
        Human-readable label
    """
    # Remove common prefixes
    label = transition_name
    for prefix in ["transition_to_", "transition_", "do_"]:
        if label.startswith(prefix):
            label = label[len(prefix) :]
            break

    # Remove past tense suffix and capitalize
    # e.g., 'approved' -> 'Approve'
    if label.endswith("ed") and len(label) > 3:
        # Handle special cases
        if label.endswith("ied"):
            label = label[:-3] + "y"
        elif label[-3] == label[-4]:  # doubled consonant (e.g., 'submitted')
            label = label[:-3]
        else:
            label = label[:-1]  # Remove 'd'
            if label.endswith("e"):
                pass  # Keep the 'e' for words like 'approve'
            else:
                label = label[:-1]  # Remove 'e' for words like 'rejected' -> 'reject'

    # Replace underscores with spaces and title case
    label = label.replace("_", " ").title()

    return label


# =============================================================================
# Registration
# =============================================================================


# Ensure all tags and filters are registered
__all__ = [
    # Filters
    "get_state_value",
    "get_state_display",
    "get_state_choice",
    "app_label",
    "model_name",
    "default_target_id",
    # Tags
    "get_available_transitions",
    "can_transition",
    "get_transition_url",
    # Inclusion tags
    "render_state_actions",
    "render_status_with_actions",
]
