"""
Meta tag utilities for the ThrillWiki application.

This module provides helper functions for generating consistent meta tags,
Open Graph data, and canonical URLs for SEO and social sharing.

Usage Examples:
    >>> from apps.core.utils.meta import generate_meta_description, get_og_image
    >>> description = generate_meta_description(park)
    >>> # Returns: 'Cedar Point is a world-famous amusement park located in Sandusky, Ohio...'

    >>> og_image = get_og_image(park)
    >>> # Returns: URL to the park's featured image or default OG image
"""

from __future__ import annotations

import re
from typing import TYPE_CHECKING, Any
from urllib.parse import urljoin

from django.conf import settings
from django.templatetags.static import static

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


def generate_meta_description(
    instance: Model | None = None,
    text: str | None = None,
    max_length: int = 160,
    fallback: str = "ThrillWiki - Your comprehensive guide to theme parks and roller coasters",
) -> str:
    """
    Generate a meta description from a model instance or text.

    Automatically truncates to max_length, preserving word boundaries
    and adding ellipsis if truncated.

    Args:
        instance: Django model instance with description/content attribute
        text: Direct text to use for description
        max_length: Maximum length for the description (default: 160)
        fallback: Fallback text if no description found

    Returns:
        Formatted meta description

    Examples:
        >>> park = Park.objects.get(slug='cedar-point')
        >>> generate_meta_description(park)
        'Cedar Point is a world-famous amusement park located in Sandusky, Ohio...'

        >>> generate_meta_description(text='A very long description that needs to be truncated...')
        'A very long description that needs to be...'
    """
    description = ""

    if text:
        description = text
    elif instance:
        # Try common description attributes
        for attr in ("description", "content", "bio", "summary", "overview"):
            value = getattr(instance, attr, None)
            if value and isinstance(value, str):
                description = value
                break

        # If no description found, try to build from name and location
        if not description:
            name = getattr(instance, "name", None)
            location = getattr(instance, "location", None)
            if name and location:
                description = f"{name} is located in {location}."
            elif name:
                description = f"Learn more about {name} on ThrillWiki."

    if not description:
        return fallback

    # Clean up the description
    description = _clean_text(description)

    # Truncate if needed
    if len(description) > max_length:
        description = _truncate_text(description, max_length)

    return description


def get_og_image(
    instance: Model | None = None,
    image_url: str | None = None,
    request: HttpRequest | None = None,
) -> str:
    """
    Get the Open Graph image URL for a model instance.

    Attempts to find an image from the model instance, falls back to
    the default OG image.

    Args:
        instance: Django model instance with image attribute
        image_url: Direct image URL to use
        request: HttpRequest for building absolute URLs

    Returns:
        Absolute URL to the Open Graph image

    Examples:
        >>> park = Park.objects.get(slug='cedar-point')
        >>> get_og_image(park, request=request)
        'https://thrillwiki.com/media/parks/cedar-point.jpg'
    """
    base_url = ""
    if request:
        base_url = f"{request.scheme}://{request.get_host()}"
    elif hasattr(settings, "FRONTEND_DOMAIN"):
        base_url = settings.FRONTEND_DOMAIN

    # Use provided image URL
    if image_url:
        if image_url.startswith(("http://", "https://")):
            return image_url
        return urljoin(base_url, image_url)

    # Try to get image from model instance
    if instance:
        for attr in ("featured_image", "image", "photo", "cover_image", "thumbnail"):
            image_field = getattr(instance, attr, None)
            if image_field:
                try:
                    if hasattr(image_field, "url"):
                        return urljoin(base_url, image_field.url)
                except ValueError:
                    continue

        # Try to get from related photos
        if hasattr(instance, "photos"):
            try:
                first_photo = instance.photos.first()
                if first_photo and hasattr(first_photo, "image"):
                    return urljoin(base_url, first_photo.image.public_url)
            except Exception:
                pass

    # Fall back to default OG image
    default_og = static("images/og-default.jpg")
    return urljoin(base_url, default_og)


def build_canonical_url(
    request: HttpRequest | None = None,
    path: str | None = None,
    instance: Model | None = None,
) -> str:
    """
    Build the canonical URL for a page.

    Args:
        request: HttpRequest for building the URL from current request
        path: Direct path to use for the canonical URL
        instance: Model instance with get_absolute_url method

    Returns:
        Canonical URL

    Examples:
        >>> build_canonical_url(request=request)
        'https://thrillwiki.com/parks/cedar-point/'

        >>> build_canonical_url(path='/parks/')
        'https://thrillwiki.com/parks/'
    """
    base_url = ""
    if hasattr(settings, "FRONTEND_DOMAIN"):
        base_url = settings.FRONTEND_DOMAIN
    elif request:
        base_url = f"{request.scheme}://{request.get_host()}"

    # Get path from various sources
    url_path = ""
    if path:
        url_path = path
    elif instance and hasattr(instance, "get_absolute_url"):
        url_path = instance.get_absolute_url()
    elif request:
        url_path = request.path

    # Remove query strings for canonical URL
    if "?" in url_path:
        url_path = url_path.split("?")[0]

    return urljoin(base_url, url_path)


def build_page_title(
    title: str,
    section: str | None = None,
    site_name: str = "ThrillWiki",
    separator: str = " - ",
) -> str:
    """
    Build a consistent page title.

    Args:
        title: Page-specific title
        section: Optional section name (e.g., 'Parks', 'Rides')
        site_name: Site name to append (default: 'ThrillWiki')
        separator: Separator between parts (default: ' - ')

    Returns:
        Formatted page title

    Examples:
        >>> build_page_title('Cedar Point', 'Parks')
        'Cedar Point - Parks - ThrillWiki'

        >>> build_page_title('Search Results')
        'Search Results - ThrillWiki'
    """
    parts = [title]
    if section:
        parts.append(section)
    parts.append(site_name)
    return separator.join(parts)


def get_twitter_card_type(
    instance: Model | None = None,
    card_type: str | None = None,
) -> str:
    """
    Determine the appropriate Twitter card type.

    Args:
        instance: Model instance to check for images
        card_type: Explicit card type to use

    Returns:
        Twitter card type ('summary_large_image' or 'summary')

    Examples:
        >>> get_twitter_card_type(park)
        'summary_large_image'  # Park has featured image

        >>> get_twitter_card_type()
        'summary'
    """
    if card_type:
        return card_type

    # Use large image card if instance has an image
    if instance:
        for attr in ("featured_image", "image", "photo", "cover_image"):
            image_field = getattr(instance, attr, None)
            if image_field:
                try:
                    if hasattr(image_field, "url") and image_field.url:
                        return "summary_large_image"
                except ValueError:
                    continue

    return "summary"


def build_meta_context(
    title: str,
    description: str | None = None,
    instance: Model | None = None,
    request: HttpRequest | None = None,
    section: str | None = None,
    og_type: str = "website",
    twitter_card: str | None = None,
) -> dict[str, Any]:
    """
    Build a complete meta context dictionary for templates.

    Args:
        title: Page title
        description: Meta description (auto-generated if None)
        instance: Model instance for generating meta data
        request: HttpRequest for URLs
        section: Section name for title
        og_type: Open Graph type (default: 'website')
        twitter_card: Twitter card type (auto-detected if None)

    Returns:
        Dictionary with all meta tag values

    Examples:
        >>> context = build_meta_context(
        ...     title='Cedar Point',
        ...     instance=park,
        ...     request=request,
        ...     section='Parks',
        ...     og_type='place',
        ... )
    """
    return {
        "page_title": build_page_title(title, section),
        "meta_description": generate_meta_description(instance, description),
        "canonical_url": build_canonical_url(request, instance=instance),
        "og_type": og_type,
        "og_title": title,
        "og_description": generate_meta_description(instance, description),
        "og_image": get_og_image(instance, request=request),
        "twitter_card": get_twitter_card_type(instance, twitter_card),
        "twitter_title": title,
        "twitter_description": generate_meta_description(instance, description),
    }


def _clean_text(text: str) -> str:
    """
    Clean text for use in meta tags.

    Removes HTML tags, extra whitespace, and normalizes line breaks.
    """
    # Remove HTML tags
    text = re.sub(r"<[^>]+>", "", text)
    # Replace multiple whitespace with single space
    text = re.sub(r"\s+", " ", text)
    # Strip leading/trailing whitespace
    text = text.strip()
    return text


def _truncate_text(text: str, max_length: int) -> str:
    """
    Truncate text at word boundary with ellipsis.
    """
    if len(text) <= max_length:
        return text

    # Truncate at word boundary
    truncated = text[: max_length - 3].rsplit(" ", 1)[0]
    return f"{truncated}..."
