import contextlib
from dataclasses import dataclass
from typing import Any

from django.contrib.sites.shortcuts import get_current_site
from django.core.exceptions import FieldError
from django.core.paginator import Paginator
from django.db.models import Model
from django.http import HttpRequest
from django.http import HttpResponse
from django.urls import reverse
from zen_queries import queries_disabled
from zen_queries import render as zen_queries_render


@dataclass
class Row:
    """
    A row in a table.
    """

    data: dict[str, str]
    actions: list[dict[str, str]]


@dataclass
class RowAction:
    """
    An action that can be performed on a row in a table.
    """

    label: str
    url_name: str
    url_kwargs: dict[str, str]

    def render(self, obj) -> dict[str, str]:
        """
        Render the action as a dictionary for the given object.
        """
        url = reverse(
            self.url_name,
            kwargs={key: getattr(obj, value) for key, value in self.url_kwargs.items()},
        )
        return {"label": self.label, "url": url}


def render_list(
    request: HttpRequest,
    entity_name: str,
    entity_name_plural: str,
    objects: list["Model"],
    columns: list[tuple[str, str]],
    row_actions: list[RowAction] = None,
    list_actions: list[tuple[str, str]] = None,
    paginate_by: int = None,
) -> HttpResponse:
    """
    Render a list of objects with a table.
    """

    # TODO: List actions

    total_count = len(objects)

    order_by = request.GET.get("order_by")

    if order_by:
        with contextlib.suppress(FieldError):
            objects = objects.order_by(order_by)

    if paginate_by:
        paginator = Paginator(object_list=objects, per_page=paginate_by)
        page = paginator.get_page(request.GET.get("page"))
        objects = page.object_list

    rows = []
    for obj in objects:
        with queries_disabled():
            row = Row(
                data={column: getattr(obj, column[0]) for column in columns},
                actions=[action.render(obj) for action in row_actions],
            )
            rows.append(row)

    context = {
        "rows": rows,
        "columns": columns,
        "row_actions": row_actions,
        "list_actions": list_actions,
        "total_count": total_count,
        "order_by": order_by,
        "entity_name": entity_name,
        "entity_name_plural": entity_name_plural,
    }

    if paginate_by:
        context |= {
            "page": page,
            "is_paginated": True,
        }

    return render(
        request=request,
        template_name="utils/list.html",
        context=context,
    )


def base_context(request: HttpRequest) -> dict[str, Any]:
    """
    Return a base context for all views.
    """
    return {"site": get_current_site(request)}


def render(request, template_name, context=None):
    """
    Render a template with a base context.
    """
    if context is None:
        context = {}
    context = base_context(request) | context
    return zen_queries_render(request, template_name, context)
