"""
Search routes for StarPunk

Provides both API and HTML endpoints for full-text search functionality.
"""

import logging
from pathlib import Path

from flask import Blueprint, current_app, g, jsonify, render_template, request

from starpunk.search import has_fts_table, search_notes

logger = logging.getLogger(__name__)

bp = Blueprint("search", __name__)


@bp.route("/api/search", methods=["GET"])
def api_search():
    """
    Search API endpoint

    Query Parameters:
        q (required): Search query string
        limit (optional): Results limit, default 20, max 100
        offset (optional): Pagination offset, default 0

    Returns:
        JSON response with search results

    Status Codes:
        200: Success (even with 0 results)
        400: Bad request (empty query)
        503: Service unavailable (FTS5 not available)
    """
    # Extract and validate query parameter
    query = request.args.get("q", "").strip()
    if not query:
        return (
            jsonify(
                {
                    "error": "Missing required parameter: q",
                    "message": "Search query cannot be empty",
                }
            ),
            400,
        )

    # Parse limit with bounds checking
    try:
        limit = min(int(request.args.get("limit", 20)), 100)
        if limit < 1:
            limit = 20
    except ValueError:
        limit = 20

    # Parse offset
    try:
        offset = max(int(request.args.get("offset", 0)), 0)
    except ValueError:
        offset = 0

    # Check if user is authenticated (for unpublished notes)
    # Anonymous users (g.me not set) see only published notes
    published_only = not hasattr(g, "me") or g.me is None

    db_path = Path(current_app.config["DATABASE_PATH"])

    # Check FTS availability
    if not has_fts_table(db_path):
        return (
            jsonify(
                {
                    "error": "Search unavailable",
                    "message": "Full-text search is not configured on this server",
                }
            ),
            503,
        )

    try:
        results = search_notes(
            query=query,
            db_path=db_path,
            published_only=published_only,
            limit=limit,
            offset=offset,
        )
    except Exception as e:
        current_app.logger.error(f"Search failed: {e}")
        return (
            jsonify(
                {"error": "Search failed", "message": "An error occurred during search"}
            ),
            500,
        )

    # Format response
    response = {
        "query": query,
        "count": len(results),
        "limit": limit,
        "offset": offset,
        "results": [
            {
                "slug": r["slug"],
                "title": r["title"] or f"Note from {r['created_at'][:10]}",
                "excerpt": r["snippet"],  # Already has <mark> tags
                "published_at": r["created_at"],
                "url": f"/notes/{r['slug']}",
            }
            for r in results
        ],
    }

    return jsonify(response), 200


@bp.route("/search")
def search_page():
    """
    Search results HTML page

    Query Parameters:
        q: Search query string
        offset: Pagination offset
    """
    query = request.args.get("q", "").strip()
    limit = 20  # Fixed for HTML view

    # Parse offset
    try:
        offset = max(int(request.args.get("offset", 0)), 0)
    except ValueError:
        offset = 0

    # Check authentication for unpublished notes
    # Anonymous users (g.me not set) see only published notes
    published_only = not hasattr(g, "me") or g.me is None

    results = []
    error = None

    if query:
        db_path = Path(current_app.config["DATABASE_PATH"])

        if not has_fts_table(db_path):
            error = "Full-text search is not configured on this server"
        else:
            try:
                results = search_notes(
                    query=query,
                    db_path=db_path,
                    published_only=published_only,
                    limit=limit,
                    offset=offset,
                )
                # Format results for template
                # Format results and escape HTML in excerpts for safety
                # FTS5 snippet() returns content with <mark> tags but doesn't escape HTML
                # We need to escape it but preserve the <mark> tags
                from markupsafe import escape, Markup

                formatted_results = []
                for r in results:
                    # Escape the snippet but allow <mark> tags
                    snippet = r["snippet"]
                    # Simple approach: escape all HTML, then unescape our mark tags
                    escaped = escape(snippet)
                    # Replace escaped mark tags with real ones
                    safe_snippet = str(escaped).replace("&lt;mark&gt;", "<mark>").replace("&lt;/mark&gt;", "</mark>")

                    formatted_results.append({
                        "slug": r["slug"],
                        "title": r["title"] or f"Note from {r['created_at'][:10]}",
                        "excerpt": Markup(safe_snippet),  # Mark as safe since we've escaped it ourselves
                        "published_at": r["created_at"],
                        "url": f"/notes/{r['slug']}",
                    })
                results = formatted_results
            except Exception as e:
                current_app.logger.error(f"Search failed: {e}")
                error = "An error occurred during search"

    return render_template(
        "search.html",
        query=query,
        results=results,
        error=error,
        limit=limit,
        offset=offset,
    )
