"""
This module contains custom template tags for the powercrud package.

It includes functions for generating action links, displaying object details,
and rendering object lists with customized formatting.

Key components:
- action_links: Generates HTML for action buttons (View, Edit, Delete, etc.)
- object_detail: Renders details of an object, including fields and properties
- object_list: Creates a list view of objects with customized field display
- get_proper_elided_page_range: Generates a properly elided page range for pagination

The module adapts to different CSS frameworks and supports HTMX and modal functionality.
"""

from datetime import date, datetime  # Import both date and datetime classes
from typing import Any, Dict, List, Optional

from django import template
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
from django.db import models

from powercrud.conf import get_powercrud_setting
from powercrud.logging import get_logger

log = get_logger(__name__)

register = template.Library()


def _should_center_field(model_field: models.Field) -> bool:
    """
    Return True when the column should default to centered alignment.
    Treat text-like, dropdown (FK), and M2M fields as left-aligned.
    """
    if not model_field:
        return False
    if getattr(model_field, "many_to_many", False):
        return False
    if model_field.is_relation:
        return False

    field_type = model_field.get_internal_type()
    text_types = {
        "CharField",
        "TextField",
        "SlugField",
        "EmailField",
        "URLField",
    }
    if field_type in text_types:
        return False
    # Default to centered for numerics, booleans, and other non-text columns.
    return True


@register.filter
def get_form_field(form, field_name):
    """
    Safely fetch a bound form field by name.
    Returns None if the field does not exist.
    """
    if not form or not field_name:
        return None
    try:
        return form[field_name]
    except KeyError:
        return None


def _resolve_modal_action_url(
    url: str, query_string: str, show_modal: bool
) -> str:
    """
    Append the current query string to modal action URLs exactly once.
    """
    if not show_modal or not query_string:
        return url
    separator = "&" if "?" in url else "?"
    return f"{url}{separator}{query_string}"


def _render_action_anchor(
    *,
    url: str,
    anchor_text: str,
    class_name: str,
    target: str,
    hx_post: bool,
    show_modal: bool,
    modal_attrs: str,
    disable: bool,
    lock_label: str | None,
    use_htmx: bool,
    query_string: str,
) -> str:
    """
    Render a single row-action anchor with HTMX and disabled metadata.
    """
    resolved_url = _resolve_modal_action_url(url, query_string, show_modal)
    disabled_classes = (
        " btn-disabled opacity-50 pointer-events-none" if disable else ""
    )

    attrs = [
        f"href='{resolved_url}'",
        f"class='{class_name}{disabled_classes}'",
    ]

    if use_htmx:
        attrs.append(f"hx-{'post' if hx_post else 'get'}='{resolved_url}'")
        if target:
            attrs.append(f"hx-target='{target}'")
        if not show_modal:
            attrs.append("hx-replace-url='true'")
            attrs.append("hx-push-url='true'")

    if show_modal and modal_attrs:
        attrs.append(modal_attrs)

    if disable and lock_label:
        attrs.append("aria-disabled='true'")
        attrs.append(f"data-tippy-content='{lock_label}'")
        attrs.append("data-powercrud-tooltip='semantic'")

    attrs.append(f"data-inline-action='{anchor_text.lower()}'")
    return f"<a {' '.join(attrs)}>{anchor_text}</a>"


def _resolve_view_option(
    view: Any,
    *,
    method_name: str,
    attr_name: str | None = None,
    default: Any = None,
) -> Any:
    """
    Return a view option from a method when available, otherwise fall back to
    a plain attribute or the supplied default.

    Template tags are sometimes exercised with lightweight stubs in tests or by
    downstream callers that do not inherit the full PowerCRUD mixin stack.
    """
    resolver = getattr(view, method_name, None)
    if callable(resolver):
        return resolver()
    if attr_name:
        return getattr(view, attr_name, default)
    return default


def action_links(view: Any, object: Any) -> str:
    """
    Generate HTML for action links (buttons) for a given object.

    Args:
        view: The view instance
        object: The object for which actions are being generated

    Returns:
        str: HTML string of action buttons
    """
    framework: str = get_powercrud_setting("POWERCRUD_CSS_FRAMEWORK")
    styles: Dict[str, Any] = view.get_framework_styles()[framework]
    action_button_classes = view.get_action_button_classes()

    prefix: str = view.get_prefix()
    use_htmx: bool = view.get_use_htmx()
    use_modal: bool = view.get_use_modal()
    extra_actions_mode: str = _resolve_view_option(
        view,
        method_name="get_extra_actions_mode",
        attr_name="extra_actions_mode",
        default="buttons",
    )
    query_string = ""
    if hasattr(view, "request") and hasattr(view.request, "GET") and view.request.GET:
        query_string = view.request.GET.urlencode()

    default_target: str = _resolve_view_option(
        view,
        method_name="get_htmx_target",
        attr_name="default_htmx_target",
        default="#content",
    )

    # Standard actions with framework-specific button classes
    lock_reason = getattr(object, "_blocked_reason", None)
    lock_label = getattr(object, "_blocked_label", None)

    standard_action_items: List[Dict[str, Any]] = []
    standard_actions = [
        ("View", view.safe_reverse(f"{prefix}-detail", kwargs={"pk": object.pk})),
        ("Edit", view.safe_reverse(f"{prefix}-update", kwargs={"pk": object.pk})),
        ("Delete", view.safe_reverse(f"{prefix}-delete", kwargs={"pk": object.pk})),
    ]
    for name, url in standard_actions:
        if url is None:
            continue
        disable = bool(lock_reason and name in {"Edit", "Delete"})
        standard_action_items.append(
            {
                "url": url,
                "text": name,
                "button_class": styles["actions"][name],
                "target": default_target,
                "hx_post": False,
                "show_modal": use_modal,
                "modal_attrs": styles["modal_attrs"],
                "disable": disable,
            }
        )

    # Add extra actions if defined
    extra_actions: List[Dict[str, Any]] = getattr(view, "extra_actions", [])
    extra_action_items: List[Dict[str, Any]] = []
    for action in extra_actions:
        url: Optional[str] = view.safe_reverse(
            action["url_name"],
            kwargs={"pk": object.pk} if action.get("needs_pk", True) else None,
        )

        if url is not None:
            htmx_target: str = action.get("htmx_target", default_target)
            if htmx_target and not htmx_target.startswith("#"):
                htmx_target = f"#{htmx_target}"
            button_class: str = action.get("button_class", styles["extra_default"])

            display_modal = action.get("display_modal", use_modal)
            show_modal: bool = display_modal if use_modal else False
            modal_attrs: str = styles["modal_attrs"] if show_modal else " "

            disable_extra = bool(lock_reason and action.get("lock_sensitive", False))

            extra_action_items.append(
                {
                    "url": url,
                    "text": action["text"],
                    "button_class": button_class,
                    "target": htmx_target,
                    "hx_post": action.get("hx_post", False),
                    "show_modal": show_modal,
                    "modal_attrs": modal_attrs,
                    "disable": disable_extra,
                }
            )

    standard_links = [
        _render_action_anchor(
            url=action["url"],
            anchor_text=action["text"],
            class_name=(
                f"{styles['base']} join-item {action['button_class']} {action_button_classes}"
            ),
            target=action["target"],
            hx_post=action["hx_post"],
            show_modal=action["show_modal"],
            modal_attrs=action["modal_attrs"],
            disable=action["disable"],
            lock_label=lock_label,
            use_htmx=use_htmx,
            query_string=query_string,
        )
        for action in standard_action_items
    ]

    if extra_actions_mode == "dropdown" and extra_action_items:
        dropdown_items = [
            "<li>"
            + _render_action_anchor(
                url=action["url"],
                anchor_text=action["text"],
                class_name="justify-start whitespace-nowrap",
                target=action["target"],
                hx_post=action["hx_post"],
                show_modal=action["show_modal"],
                modal_attrs=action["modal_attrs"],
                disable=action["disable"],
                lock_label=lock_label,
                use_htmx=use_htmx,
                query_string=query_string,
            )
            + "</li>"
            for action in extra_action_items
        ]
        extra_links = [
            "<div class='dropdown dropdown-end'>"
            f"<div tabindex='0' role='button' class='{styles['base']} join-item {styles['extra_default']} {action_button_classes} gap-1' aria-label='More actions' data-inline-action='more'>More"
            "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='currentColor' class='h-3.5 w-3.5' aria-hidden='true'>"
            "<path fill-rule='evenodd' d='M5.23 7.21a.75.75 0 0 1 1.06.02L10 11.168l3.71-3.938a.75.75 0 1 1 1.08 1.04l-4.25 4.5a.75.75 0 0 1-1.08 0l-4.25-4.5a.75.75 0 0 1 .02-1.06Z' clip-rule='evenodd' />"
            "</svg></div>"
            "<ul tabindex='0' class='dropdown-content menu bg-base-100 rounded-box z-[10] min-w-40 p-2 shadow border border-base-300'>"
            + "".join(dropdown_items)
            + "</ul></div>"
        ]
    else:
        extra_links = [
            _render_action_anchor(
                url=action["url"],
                anchor_text=action["text"],
                class_name=(
                    f"{styles['base']} join-item {action['button_class']} {action_button_classes}"
                ),
                target=action["target"],
                hx_post=action["hx_post"],
                show_modal=action["show_modal"],
                modal_attrs=action["modal_attrs"],
                disable=action["disable"],
                lock_label=lock_label,
                use_htmx=use_htmx,
                query_string=query_string,
            )
            for action in extra_action_items
        ]

    links: List[str] = ["<div class='join'>" + " ".join(standard_links + extra_links) + "</div>"]

    return mark_safe(" ".join(links))


@register.inclusion_tag(
    f"powercrud/{get_powercrud_setting('POWERCRUD_CSS_FRAMEWORK')}/partial/detail.html"
)
def object_detail(object, view):
    """
    Display both fields and properties for an object detail view.

    Args:
        object: The object to display
        view: The view instance

    Returns:
        dict: Context for rendering the detail template
    """

    def iter():
        # Handle regular fields
        for f in view.detail_fields:
            field = object._meta.get_field(f)
            if field.is_relation:
                value = str(getattr(object, f))
            else:
                value = field.value_to_string(object)
            yield (field.verbose_name, value)

        # Handle properties
        for prop in view.detail_properties:
            value = str(getattr(object, prop))
            name = prop.replace("_", " ").title()
            yield (name, value)

    return {
        "object": iter(),
    }


@register.inclusion_tag(
    f"powercrud/{get_powercrud_setting('POWERCRUD_CSS_FRAMEWORK')}/partial/list.html",
    takes_context=True,
)
def object_list(context, objects, view):
    """
    Override default to set value = str()
    instead of value_to_string(). This allows related fields
    to be displayed correctly (not just the id)
    """
    fields = view.fields
    properties = getattr(view, "properties", []) or []

    inline_config = context.get("inline_edit") or {}
    inline_enabled = bool(inline_config.get("enabled"))
    inline_fields = inline_config.get("fields") or []
    inline_fields = set(inline_fields)
    inline_row_endpoint = inline_config.get("row_endpoint_name")
    inline_dependencies = inline_config.get("dependencies") or {}

    # Check if bulk edit is enabled
    enable_bulk_edit = (
        hasattr(view, "get_bulk_edit_enabled") and view.get_bulk_edit_enabled()
    )

    # Get currently selected IDs from session if bulk edit is enabled
    request = context.get("request") or getattr(view, "request", None)
    selected_ids = []
    if request and enable_bulk_edit and hasattr(view, "get_selected_ids_from_session"):
        selected_ids = view.get_selected_ids_from_session(request)
        # Convert to strings for comparison
        selected_ids = [str(id) for id in selected_ids]

    # Get selection key suffix if available
    selection_key_suffix = ""
    if enable_bulk_edit and hasattr(view, "get_bulk_selection_key_suffix"):
        selection_key_suffix = view.get_bulk_selection_key_suffix()

    # Create tuples of (display_name, field_name, is_sortable) for each field
    headers = []
    for f in fields:
        try:
            field = view.model._meta.get_field(f)
            # Handle M2M fields differently
            if (
                hasattr(field, "remote_field")
                and field.remote_field
                and isinstance(field.remote_field, models.ManyToManyRel)
            ):
                display_name = (
                    field.remote_field.model._meta.verbose_name_plural.title()
                )
            else:
                display_name = (
                    field.verbose_name.title()
                    if hasattr(field, "verbose_name") and field.verbose_name
                    else f.replace("_", " ").title()
                )
            headers.append((display_name, f, True))  # Regular fields are sortable
        except Exception as e:
            log.warning(f"Error processing field {f}: {str(e)}")
            # Fallback to basic field name formatting
            headers.append((f.replace("_", " ").title(), f, True))

    # Add properties with proper display names (not sortable)
    for prop in properties:
        # Try to get short_description from property
        prop_obj = getattr(view.model, prop, None)
        if (
            prop_obj
            and hasattr(prop_obj.fget, "short_description")
            and prop_obj.fget.short_description
        ):
            display_name = prop_obj.fget.short_description
        else:
            display_name = prop.replace("_", " ").title()
        headers.append((display_name, prop, False))  # Properties are not sortable

    TICK_SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="green" class="size-4 inline-block"><path fill-rule="evenodd" d="M8 15A7 7 0 1 0 8 1a7 7 0 0 0 0 14Zm3.844-8.791a.75.75 0 0 0-1.188-.918l-3.7 4.79-1.649-1.833a.75.75 0 1 0-1.114 1.004l2.25 2.5a.75.75 0 0 0 1.15-.043l4.25-5.5Z" clip-rule="evenodd" /></svg>'
    CROSS_SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="crimson" class="size-4 inline-block"><path fill-rule="evenodd" d="M8 15A7 7 0 1 0 8 1a7 7 0 0 0 0 14Zm2.78-4.22a.75.75 0 0 1-1.06 0L8 9.06l-1.72 1.72a.75.75 0 1 1-1.06-1.06L6.94 8 5.22 6.28a.75.75 0 0 1 1.06-1.06L8 6.94l1.72-1.72a.75.75 0 1 1 1.06 1.06L9.06 8l1.72 1.72a.75.75 0 0 1 0 1.06Z" clip-rule="evenodd" /></svg>'

    object_list = []
    for obj in objects:
        row_id = getattr(view, "get_inline_row_id", None)
        if callable(row_id):
            row_id = row_id(obj)
        else:
            row_id = None

        inline_row_url = None
        if inline_enabled and inline_row_endpoint:
            inline_row_url = view.safe_reverse(
                inline_row_endpoint, kwargs={"pk": obj.pk}
            )

        inline_blocked_reason = None
        inline_blocked_label = None
        inline_blocked_meta = {}
        inline_allowed = False

        if inline_enabled and inline_row_url:
            lock_checker = getattr(view, "is_inline_row_locked", None)
            is_locked = False
            if callable(lock_checker):
                try:
                    is_locked = bool(lock_checker(obj))
                except Exception:
                    is_locked = False

            can_inline_callable = getattr(view, "can_inline_edit", None)
            can_inline = inline_enabled
            if callable(can_inline_callable) and request is not None:
                try:
                    can_inline = bool(can_inline_callable(obj, request))
                except Exception:
                    can_inline = False
            elif not callable(can_inline_callable):
                can_inline = inline_enabled
            else:
                can_inline = False

            if is_locked:
                lock_details = {}
                lock_detail_getter = getattr(view, "get_inline_lock_details", None)
                if callable(lock_detail_getter):
                    try:
                        lock_details = lock_detail_getter(obj) or {}
                    except Exception:
                        lock_details = {}
                inline_blocked_meta = lock_details
                inline_blocked_reason = "locked"
                inline_blocked_label = lock_details.get("label") or _(
                    "Inline editing blocked – record is locked."
                )
            elif not can_inline:
                inline_blocked_reason = "forbidden"
                inline_blocked_label = _("Inline editing not permitted for this row.")
            else:
                inline_allowed = True

        row_dependency_endpoint_url = None
        dependency_endpoint_name = inline_config.get("dependency_endpoint_name")
        if inline_enabled and dependency_endpoint_name and getattr(obj, "pk", None):
            row_dependency_endpoint_url = view.safe_reverse(
                dependency_endpoint_name,
                kwargs={"pk": obj.pk},
            ) or view.safe_reverse(dependency_endpoint_name)

        def resolve_cell_dependency(field_name: str):
            dependency = inline_dependencies.get(field_name)
            if not dependency:
                return None
            resolved = dict(dependency)
            endpoint_name = resolved.get("endpoint_name")
            endpoint_url = resolved.get("endpoint_url")
            if endpoint_name and getattr(obj, "pk", None):
                endpoint_url = view.safe_reverse(
                    endpoint_name,
                    kwargs={"pk": obj.pk},
                ) or view.safe_reverse(endpoint_name)
            if not endpoint_url:
                endpoint_url = row_dependency_endpoint_url
            resolved["endpoint_url"] = endpoint_url
            return resolved

        # Attach inline metadata to the object for downstream helpers (e.g., action_links)
        setattr(obj, "_blocked_reason", inline_blocked_reason)
        setattr(obj, "_blocked_label", inline_blocked_label)

        record = {
            "object": obj,
            "id": str(obj.pk),  # Add ID for selection tracking
            "is_selected": str(obj.pk) in selected_ids
            if enable_bulk_edit
            else False,  # Check if this object is selected
            "row_id": row_id,
            "inline_url": inline_row_url,
            "inline_allowed": inline_allowed,
            "inline_blocked_reason": inline_blocked_reason,
            "inline_blocked_label": inline_blocked_label,
            "inline_blocked_meta": inline_blocked_meta,
            "cells": [],
            "fields": [],
            "actions": action_links(view, obj),
        }

        for f in fields:
            model_field = obj._meta.get_field(f)
            if model_field.many_to_many:
                display_value = ", ".join(str(item) for item in getattr(obj, f).all())
            elif model_field.get_internal_type() == "BooleanField":
                value = getattr(obj, f)
                if value is True:
                    display_value = mark_safe(TICK_SVG)
                elif value is False:
                    display_value = mark_safe(CROSS_SVG)
                else:
                    display_value = ""
            elif (
                model_field.get_internal_type() == "DateField"
                and getattr(obj, f) is not None
            ):
                display_value = getattr(obj, f).strftime("%d/%m/%Y")
            elif model_field.is_relation:
                display_value = str(getattr(obj, f))
            else:
                display_value = model_field.value_to_string(obj)

            cell_align = "center" if _should_center_field(model_field) else "left"

            record["cells"].append(
                {
                    "name": f,
                    "value": display_value,
                    "is_property": False,
                    "is_inline_editable": inline_enabled and f in inline_fields,
                    "dependency": resolve_cell_dependency(f),
                    "align": cell_align,
                }
            )
            record["fields"].append(display_value)

        for prop in properties:
            prop_value = getattr(obj, prop)
            prop_align = "center" if isinstance(prop_value, bool) else "left"
            if (
                isinstance(getattr(obj.__class__, prop), property)
                and prop_value is True
            ):
                display_value = mark_safe(TICK_SVG)
            elif (
                isinstance(getattr(obj.__class__, prop), property)
                and prop_value is False
            ):
                display_value = mark_safe(CROSS_SVG)
            elif isinstance(prop_value, (date, datetime)) and prop_value is not None:
                display_value = prop_value.strftime("%d/%m/%Y")
            else:
                display_value = str(prop_value)

            record["cells"].append(
                {
                    "name": prop,
                    "value": display_value,
                    "is_property": True,
                    "is_inline_editable": inline_enabled and prop in inline_fields,
                    "dependency": resolve_cell_dependency(prop),
                    "align": prop_align,
                }
            )
            record["fields"].append(display_value)

        object_list.append(record)

    request = context.get("request")
    current_sort = request.GET.get("sort", "") if request else ""

    # Get all current filter parameters
    filter_params = request.GET.copy() if request else {}
    if "sort" in filter_params:
        filter_params.pop("sort")
    if "page" in filter_params:
        filter_params.pop("page")

    # Only keep the last value for each key to avoid duplicate params in URLs
    clean_params = {}
    for k in filter_params:
        clean_params[k] = filter_params.getlist(k)[-1]
    filter_params = filter_params.__class__("", mutable=True)
    for k, v in clean_params.items():
        filter_params[k] = v

    use_htmx = context.get("use_htmx", view.get_use_htmx())
    original_target = context.get("original_target", view.get_original_target())
    htmx_target = context.get("htmx_target", view.get_htmx_target())

    return {
        "headers": headers,  # Now contains tuples of (display_name, field_name, is_sortable)
        "object_list": object_list,
        "current_sort": current_sort,
        "filter_params": filter_params.urlencode(),  # Add filter parameters to context
        "use_htmx": use_htmx,
        "original_target": original_target,
        "table_pixel_height_other_page_elements": view.get_table_pixel_height_other_page_elements(),
        "table_max_height": view.get_table_max_height(),
        "table_classes": view.get_table_classes(),
        "htmx_target": htmx_target,
        "request": request,
        # add bulk selection context
        "selected_ids": selected_ids,
        # Add bulk edit related context
        "enable_bulk_edit": enable_bulk_edit,
        "selected_count": len(selected_ids) if enable_bulk_edit else 0,
        "model_name": view.model.__name__.lower() if hasattr(view, "model") else "",
        "selection_key_suffix": selection_key_suffix,
        "inline_edit": inline_config,
    }


@register.simple_tag
def get_proper_elided_page_range(paginator, number, on_each_side=1, on_ends=1):
    """
    Return a list of page numbers with proper elision for pagination.

    Args:
        paginator: The Django Paginator instance
        number: The current page number
        on_each_side: Number of pages to show on each side of the current page
        on_ends: Number of pages to show at the beginning and end of the range

    Returns:
        list: A list of page numbers and ellipsis characters
    """
    page_range = paginator.get_elided_page_range(
        number=number, on_each_side=1, on_ends=1
    )
    return page_range


@register.simple_tag
def extra_buttons(view: Any) -> str:
    """
    Generate HTML for extra buttons in the list view header.

    Args:
        view: The view instance

    Returns:
        str: HTML string of extra buttons
    """
    framework: str = get_powercrud_setting("POWERCRUD_CSS_FRAMEWORK")
    styles: Dict[str, Any] = view.get_framework_styles()[framework]

    use_htmx: bool = view.get_use_htmx()
    use_modal: bool = view.get_use_modal()

    extra_buttons: List[Dict[str, Any]] = getattr(view, "extra_buttons", [])
    extra_button_classes = _resolve_view_option(
        view,
        method_name="get_extra_button_classes",
        attr_name="extra_button_classes",
        default="",
    )

    buttons: List[str] = []
    for button in extra_buttons:
        display_modal = button.get("display_modal", False) and use_modal
        modal_attrs = ""
        extra_attrs = button.get("extra_attrs", "")
        extra_class_attrs = button.get("extra_class_attrs", "")

        url: Optional[str] = view.safe_reverse(
            button["url_name"], kwargs={} if not button.get("needs_pk", False) else None
        )
        if url is not None:
            htmx_attrs = []
            if use_htmx:
                if display_modal:
                    htmx_target = _resolve_view_option(
                        view,
                        method_name="get_modal_target",
                        attr_name="modal_target",
                        default="#powercrudModalContent",
                    )
                    if htmx_target and not htmx_target.startswith("#"):
                        htmx_target = f"#{htmx_target}"
                    modal_attrs = styles.get("modal_attrs", "")
                else:
                    htmx_target = button.get("htmx_target", "")
                    if htmx_target and not htmx_target.startswith("#"):
                        htmx_target = f"#{htmx_target}"

                htmx_attrs.append(f'hx-get="{url}"')
                if htmx_target:
                    htmx_attrs.append(f'hx-target="{htmx_target}"')
                if use_htmx and not display_modal:
                    htmx_attrs.append('hx-replace-url="true"')
                    htmx_attrs.append('hx-push-url="true"')

            htmx_attrs_str = " ".join(htmx_attrs)

            button_class = button.get("button_class", styles["extra_default"])

            new_button = (
                f'<a href="{url}" '
                f'class="{extra_class_attrs} {styles["base"]} {extra_button_classes} {button_class}" '
                f"{extra_attrs} {htmx_attrs_str} "
                f"{modal_attrs}>"
                f"{button['text']}</a>"
            )

            buttons.append(new_button)

    if buttons:
        return mark_safe(" ".join(buttons))
    return ""


@register.simple_tag(takes_context=True)
def get_powercrud_session_data(context, key):
    """
    Get a value from the powercrud session data for the current model.

    Usage in template:
    {% get_powercrud_session_data 'original_template' as template_name %}
    """
    request = context.get("request")
    view = context.get("view")

    if not request or not view:
        return None

    powercrud_data = request.session.get("powercrud", {})
    model_key = view.get_model_session_key()
    model_data = powercrud_data.get(model_key, {})

    return model_data.get(key)
