from __future__ import annotations

from quart import Blueprint, g, redirect, url_for, make_response
from sqlalchemy import select, func, or_, cast, String, exists
from sqlalchemy.orm import selectinload


from shared.models.order import Order, OrderItem
from shared.browser.app.payments.sumup import create_checkout as sumup_create_checkout
from shared.config import config

from shared.infrastructure.http_utils import vary as _vary, current_url_without_page as _current_url_without_page
from shared.infrastructure.cart_identity import current_cart_identity
from bp.cart.services import check_sumup_status
from shared.browser.app.utils.htmx import is_htmx_request
from shared.sx.helpers import sx_response
from bp import register_order

from .filters.qs import makeqs_factory, decode


def register(url_prefix: str) -> Blueprint:
    bp = Blueprint("orders", __name__, url_prefix=url_prefix)
    bp.register_blueprint(
        register_order(),
    )
    ORDERS_PER_PAGE = 10  # keep in sync with browse page size / your preference

    oob = {
        "extends": "_types/root/_index.html",
        "child_id": "auth-header-child",
        "header": "_types/auth/header/_header.html",
        "nav": "_types/auth/_nav.html",
        "main": "_types/auth/_main_panel.html",
    }

    @bp.context_processor
    def inject_oob():
        return {"oob": oob}

    @bp.before_request
    def route():
        # this is the crucial bit for the |qs filter
        g.makeqs_factory = makeqs_factory

    @bp.before_request
    async def _require_identity():
        """Orders require a logged-in user or at least a cart session."""
        ident = current_cart_identity()
        if not ident["user_id"] and not ident["session_id"]:
            return redirect(url_for("auth.login_form"))

    @bp.get("/")
    async def list_orders():

        # --- ownership: only show orders belonging to current user/session ---
        ident = current_cart_identity()
        if ident["user_id"]:
            owner_clause = Order.user_id == ident["user_id"]
        elif ident["session_id"]:
            owner_clause = Order.session_id == ident["session_id"]
        else:
            return redirect(url_for("auth.login_form"))

        # --- decode filters from query string (page + search) ---
        q = decode()
        page, search = q.page, q.search

        # sanity clamp page
        if page < 1:
            page = 1

        # --- build where clause for search ---
        where_clause = None
        if search:
            term = f"%{search.strip()}%"
            conditions = [
                Order.status.ilike(term),
                Order.currency.ilike(term),
                Order.sumup_checkout_id.ilike(term),
                Order.sumup_status.ilike(term),
                Order.description.ilike(term),
            ]

            conditions.append(
                exists(
                    select(1)
                    .select_from(OrderItem)
                    .where(
                        OrderItem.order_id == Order.id,
                        or_(
                            OrderItem.product_title.ilike(term),
                            OrderItem.product_slug.ilike(term),
                        ),
                    )
                )
            )

            # allow exact ID match or partial (string) match
            try:
                search_id = int(search)
            except (TypeError, ValueError):
                search_id = None

            if search_id is not None:
                conditions.append(Order.id == search_id)
            else:
                conditions.append(cast(Order.id, String).ilike(term))

            where_clause = or_(*conditions)

        # --- total count & total pages (respecting search + ownership) ---
        count_stmt = select(func.count()).select_from(Order).where(owner_clause)
        if where_clause is not None:
            count_stmt = count_stmt.where(where_clause)

        total_count_result = await g.s.execute(count_stmt)
        total_count = total_count_result.scalar_one() or 0
        total_pages = max(1, (total_count + ORDERS_PER_PAGE - 1) // ORDERS_PER_PAGE)

        # clamp page if beyond range (just in case)
        if page > total_pages:
            page = total_pages

        # --- paginated orders (respecting search + ownership) ---
        offset = (page - 1) * ORDERS_PER_PAGE
        stmt = (
            select(Order)
            .where(owner_clause)
            .order_by(Order.created_at.desc())
            .offset(offset)
            .limit(ORDERS_PER_PAGE)
        )
        if where_clause is not None:
            stmt = stmt.where(where_clause)

        result = await g.s.execute(stmt)
        orders = result.scalars().all()

        from shared.sx.page import get_template_context
        from sxc.pages.renders import (
            render_orders_page,
            render_orders_rows,
            render_orders_oob,
        )

        ctx = await get_template_context()
        qs_fn = makeqs_factory()

        if not is_htmx_request():
            html = await render_orders_page(
                ctx, orders, page, total_pages, search, total_count,
                url_for, qs_fn,
            )
            resp = await make_response(html)
        elif page > 1:
            sx_src = render_orders_rows(
                ctx, orders, page, total_pages, url_for, qs_fn,
            )
            resp = sx_response(sx_src)
        else:
            sx_src = await render_orders_oob(
                ctx, orders, page, total_pages, search, total_count,
                url_for, qs_fn,
            )
            resp = sx_response(sx_src)
        resp.headers["Hx-Push-Url"] = _current_url_without_page()
        return _vary(resp)

    return bp

