"""
Breadcrumb utilities for the ThrillWiki application.

This module provides functions and classes for building breadcrumb navigation
with support for dynamic breadcrumb generation from URL patterns and model instances.

Usage Examples:
    Basic breadcrumb list:
        breadcrumbs = [
            build_breadcrumb('Home', '/'),
            build_breadcrumb('Parks', '/parks/'),
            build_breadcrumb('Cedar Point', is_current=True),
        ]

    From a model instance:
        park = Park.objects.get(slug='cedar-point')
        breadcrumbs = get_model_breadcrumb(park)
        # Returns: [Home, Parks, Cedar Point]

    Using the builder pattern:
        breadcrumbs = (
            BreadcrumbBuilder()
            .add_home()
            .add('Parks', '/parks/')
            .add_current('Cedar Point')
            .build()
        )
"""

from __future__ import annotations

import contextlib
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any
from urllib.parse import urljoin

from django.urls import reverse

if TYPE_CHECKING:
    from django.db.models import Model
    from django.http import HttpRequest


@dataclass
class Breadcrumb:
    """
    Represents a single breadcrumb item.

    Attributes:
        label: Display text for the breadcrumb
        url: URL the breadcrumb links to (None for current page)
        icon: Optional icon class (e.g., 'fas fa-home')
        is_current: Whether this is the current page (last item)
        schema_position: Position in Schema.org BreadcrumbList (1-indexed)
    """

    label: str
    url: str | None = None
    icon: str | None = None
    is_current: bool = False
    schema_position: int = 1

    def __post_init__(self) -> None:
        """Set is_current to True if no URL is provided."""
        if self.url is None:
            self.is_current = True

    @property
    def is_clickable(self) -> bool:
        """Return True if the breadcrumb should be a link."""
        return self.url is not None and not self.is_current

    def to_schema_dict(self) -> dict[str, Any]:
        """
        Return Schema.org BreadcrumbList item format.

        Returns:
            Dictionary formatted for JSON-LD structured data
        """
        item: dict[str, Any] = {
            "@type": "ListItem",
            "position": self.schema_position,
            "name": self.label,
        }
        if self.url:
            item["item"] = self.url
        return item


def build_breadcrumb(
    label: str,
    url: str | None = None,
    icon: str | None = None,
    is_current: bool = False,
) -> Breadcrumb:
    """
    Create a single breadcrumb item.

    Args:
        label: Display text for the breadcrumb
        url: URL the breadcrumb links to (None for current page)
        icon: Optional icon class (e.g., 'fas fa-home')
        is_current: Whether this is the current page

    Returns:
        Breadcrumb instance

    Examples:
        >>> build_breadcrumb('Home', '/', icon='fas fa-home')
        Breadcrumb(label='Home', url='/', icon='fas fa-home', is_current=False)

        >>> build_breadcrumb('Cedar Point', is_current=True)
        Breadcrumb(label='Cedar Point', url=None, icon=None, is_current=True)
    """
    return Breadcrumb(label=label, url=url, icon=icon, is_current=is_current)


class BreadcrumbBuilder:
    """
    Builder pattern for constructing breadcrumb lists.

    Provides a fluent API for building breadcrumb navigation with
    automatic position tracking and common patterns.

    Examples:
        >>> builder = BreadcrumbBuilder()
        >>> breadcrumbs = (
        ...     builder
        ...     .add_home()
        ...     .add('Parks', '/parks/')
        ...     .add_current('Cedar Point')
        ...     .build()
        ... )
    """

    def __init__(self, base_url: str = "") -> None:
        """
        Initialize the breadcrumb builder.

        Args:
            base_url: Base URL to prepend to all relative URLs
        """
        self._items: list[Breadcrumb] = []
        self._base_url = base_url

    def add(
        self,
        label: str,
        url: str | None = None,
        icon: str | None = None,
    ) -> BreadcrumbBuilder:
        """
        Add a breadcrumb item to the list.

        Args:
            label: Display text for the breadcrumb
            url: URL the breadcrumb links to
            icon: Optional icon class

        Returns:
            Self for method chaining
        """
        position = len(self._items) + 1
        full_url = urljoin(self._base_url, url) if url else url
        self._items.append(
            Breadcrumb(
                label=label,
                url=full_url,
                icon=icon,
                is_current=False,
                schema_position=position,
            )
        )
        return self

    def add_home(
        self,
        label: str = "Home",
        url: str = "/",
        icon: str = "fas fa-home",
    ) -> BreadcrumbBuilder:
        """
        Add the home breadcrumb (typically first item).

        Args:
            label: Home label (default: 'Home')
            url: Home URL (default: '/')
            icon: Home icon class (default: 'fas fa-home')

        Returns:
            Self for method chaining
        """
        return self.add(label, url, icon)

    def add_current(
        self,
        label: str,
        icon: str | None = None,
    ) -> BreadcrumbBuilder:
        """
        Add the current page breadcrumb (last item, non-clickable).

        Args:
            label: Display text for current page
            icon: Optional icon class

        Returns:
            Self for method chaining
        """
        position = len(self._items) + 1
        self._items.append(
            Breadcrumb(
                label=label,
                url=None,
                icon=icon,
                is_current=True,
                schema_position=position,
            )
        )
        return self

    def add_from_url(
        self,
        url_name: str,
        label: str,
        url_kwargs: dict[str, Any] | None = None,
        icon: str | None = None,
    ) -> BreadcrumbBuilder:
        """
        Add a breadcrumb using Django URL name reverse lookup.

        Args:
            url_name: Django URL name to reverse
            label: Display text for the breadcrumb
            url_kwargs: Keyword arguments for URL reverse
            icon: Optional icon class

        Returns:
            Self for method chaining
        """
        url = reverse(url_name, kwargs=url_kwargs)
        return self.add(label, url, icon)

    def add_model(
        self,
        instance: Model,
        url_attr: str = "get_absolute_url",
        label_attr: str = "name",
        icon: str | None = None,
    ) -> BreadcrumbBuilder:
        """
        Add a breadcrumb from a model instance.

        Args:
            instance: Django model instance
            url_attr: Method name to get URL (default: 'get_absolute_url')
            label_attr: Attribute name for label (default: 'name')
            icon: Optional icon class

        Returns:
            Self for method chaining
        """
        url_method = getattr(instance, url_attr, None)
        url = url_method() if callable(url_method) else None
        label = getattr(instance, label_attr, str(instance))
        return self.add(label, url, icon)

    def add_model_current(
        self,
        instance: Model,
        label_attr: str = "name",
        icon: str | None = None,
    ) -> BreadcrumbBuilder:
        """
        Add a model instance as the current page breadcrumb.

        Args:
            instance: Django model instance
            label_attr: Attribute name for label (default: 'name')
            icon: Optional icon class

        Returns:
            Self for method chaining
        """
        label = getattr(instance, label_attr, str(instance))
        return self.add_current(label, icon)

    def build(self) -> list[Breadcrumb]:
        """
        Build and return the breadcrumb list.

        Returns:
            List of Breadcrumb instances
        """
        return self._items.copy()

    def clear(self) -> BreadcrumbBuilder:
        """
        Clear all breadcrumb items.

        Returns:
            Self for method chaining
        """
        self._items = []
        return self


def get_model_breadcrumb(
    instance: Model,
    include_home: bool = True,
    parent_attr: str | None = None,
    list_url_name: str | None = None,
    list_label: str | None = None,
) -> list[Breadcrumb]:
    """
    Generate breadcrumbs for a model instance with parent relationships.

    This function automatically builds breadcrumbs by traversing parent
    relationships and including model list pages.

    Args:
        instance: Django model instance
        include_home: Include home breadcrumb (default: True)
        parent_attr: Attribute name for parent relationship (e.g., 'park' for Ride)
        list_url_name: URL name for the model's list page
        list_label: Label for the list page breadcrumb

    Returns:
        List of Breadcrumb instances

    Examples:
        >>> ride = Ride.objects.get(slug='millennium-force')
        >>> breadcrumbs = get_model_breadcrumb(
        ...     ride,
        ...     parent_attr='park',
        ...     list_url_name='rides:list',
        ...     list_label='Rides',
        ... )
        # Returns: [Home, Parks, Cedar Point, Rides, Millennium Force]
    """
    builder = BreadcrumbBuilder()

    if include_home:
        builder.add_home()

    # Add parent breadcrumbs if parent_attr is specified
    if parent_attr:
        parent = getattr(instance, parent_attr, None)
        if parent:
            # Recursively get parent's model name for list URL
            parent_model_name = parent.__class__.__name__.lower()
            parent_list_url = f"{parent_model_name}s:list"
            parent_list_label = f"{parent.__class__.__name__}s"

            with contextlib.suppress(Exception):
                builder.add_from_url(parent_list_url, parent_list_label)

            builder.add_model(parent)

    # Add list page breadcrumb
    if list_url_name and list_label:
        with contextlib.suppress(Exception):
            builder.add_from_url(list_url_name, list_label)

    # Add current model instance
    builder.add_model_current(instance)

    return builder.build()


def breadcrumbs_to_schema(
    breadcrumbs: list[Breadcrumb],
    request: HttpRequest | None = None,
) -> dict[str, Any]:
    """
    Convert breadcrumbs to Schema.org BreadcrumbList JSON-LD format.

    Args:
        breadcrumbs: List of Breadcrumb instances
        request: Optional HttpRequest for building absolute URLs

    Returns:
        Dictionary formatted for JSON-LD structured data

    Examples:
        >>> breadcrumbs = [
        ...     build_breadcrumb('Home', '/'),
        ...     build_breadcrumb('Parks', '/parks/'),
        ... ]
        >>> schema = breadcrumbs_to_schema(breadcrumbs, request)
        >>> print(json.dumps(schema, indent=2))
    """
    base_url = ""
    if request:
        base_url = f"{request.scheme}://{request.get_host()}"

    items = []
    for i, crumb in enumerate(breadcrumbs, 1):
        item: dict[str, Any] = {
            "@type": "ListItem",
            "position": i,
            "name": crumb.label,
        }
        if crumb.url:
            item["item"] = urljoin(base_url, crumb.url) if base_url else crumb.url
        items.append(item)

    return {
        "@context": "https://schema.org",
        "@type": "BreadcrumbList",
        "itemListElement": items,
    }
