from quart import request

from typing import Iterable, Optional, Union

from shared.browser.app.filters.qs_base import (
    KEEP, _norm, make_filter_set, build_qs,
)
from shared.browser.app.filters.query_types import MarketQuery


def decode() -> MarketQuery:
    page = int(request.args.get("page", 1))
    search = request.args.get("search")
    sort = request.args.get("sort")
    liked = request.args.get("liked")

    selected_brands = tuple(s.strip() for s in request.args.getlist("brand") if s.strip())
    selected_stickers = tuple(s.strip().lower() for s in request.args.getlist("sticker") if s.strip())
    selected_labels = tuple(s.strip().lower() for s in request.args.getlist("label") if s.strip())

    return MarketQuery(page, search, sort, selected_brands, selected_stickers, selected_labels, liked)


def makeqs_factory():
    """
    Build a makeqs(...) that starts from the current filters + page.
    Auto-resets page to 1 when filters change unless you pass page explicitly.
    """
    q = decode()
    base_stickers = [s for s in q.selected_stickers if (s or "").strip()]
    base_labels = [s for s in q.selected_labels if (s or "").strip()]
    base_brands = [s for s in q.selected_brands if (s or "").strip()]
    base_search = q.search or None
    base_liked = q.liked or None
    base_sort = q.sort or None
    base_page = int(q.page or 1)

    def makeqs(
        *,
        clear_filters: bool = False,
        add_sticker: Union[str, Iterable[str], None] = None,
        remove_sticker: Union[str, Iterable[str], None] = None,
        add_label: Union[str, Iterable[str], None] = None,
        remove_label: Union[str, Iterable[str], None] = None,
        add_brand: Union[str, Iterable[str], None] = None,
        remove_brand: Union[str, Iterable[str], None] = None,
        search: Union[str, None, object] = KEEP,
        sort: Union[str, None, object] = KEEP,
        page: Union[int, None, object] = None,
        extra: Optional[Iterable[tuple]] = None,
        leading_q: bool = True,
        liked: Union[bool, None, object] = KEEP,
    ) -> str:
        stickers = make_filter_set(base_stickers, add_sticker, remove_sticker, clear_filters)
        labels = make_filter_set(base_labels, add_label, remove_label, clear_filters)
        brands = make_filter_set(base_brands, add_brand, remove_brand, clear_filters)

        final_search = None if clear_filters else base_search if search is KEEP else ((search or "").strip() or None)
        final_sort = base_sort if sort is KEEP else (sort or None)
        final_liked = None if clear_filters else base_liked if liked is KEEP else liked

        # Did filters change?
        filters_changed = (
            set(map(_norm, stickers)) != set(map(_norm, base_stickers))
            or set(map(_norm, labels)) != set(map(_norm, base_labels))
            or set(map(_norm, brands)) != set(map(_norm, base_brands))
            or final_search != base_search
            or final_sort != base_sort
            or final_liked != base_liked
        )

        # Page logic
        if page is KEEP:
            final_page = 1 if filters_changed else base_page
        else:
            final_page = page

        # Build params
        params = []
        for s in stickers:
            params.append(("sticker", s))
        for s in labels:
            params.append(("label", s))
        for s in brands:
            params.append(("brand", s))
        if final_search:
            params.append(("search", final_search))
        if final_liked is not None:
            params.append(("liked", final_liked))
        if final_sort:
            params.append(("sort", final_sort))
        if final_page is not None:
            params.append(("page", str(final_page)))
        if extra:
            for k, v in extra:
                if v is not None:
                    params.append((k, str(v)))

        return build_qs(params, leading_q=leading_q)

    return makeqs
