from __future__ import annotations
import os, json
from typing import List, Optional
from shared.config import config
from .blacklist.product import is_product_blocked


def _json(path: str):
    with open(path, "r", encoding="utf-8") as f:
        return json.load(f)


def fs_nav():
    path = os.path.join(config()["cache"]["fs_root"], "nav.json")
    return _json(path)


def _brand_of(item: dict) -> str:
    b = (item.get("brand") or "").strip()
    if b:
        return b
    try:
        return (item.get("info_table", {}).get("Brand") or "").strip()
    except Exception:
        return ""


def _stickers_of(item: dict) -> List[str]:
    vals = item.get("stickers") or []
    out = []
    for v in vals:
        s = (str(v) or "").strip().lower()
        if s:
            out.append(s)
    return out


def fs_product_by_slug(slug: str):
    slug = (slug or "").strip()
    if slug.endswith(".json"):
        path = os.path.join(config()["cache"]["fs_root"], "products", slug)
    else:
        path = os.path.join(config()["cache"]["fs_root"], "products", f"{slug}.json")
    return _json(path)


def fs_count_products_in_sub(top_slug: str, sub_slug: Optional[str]) -> int:
    """
    Return how many products are in the listing for (top_slug, sub_slug),
    after filtering out blocked products.

    If sub_slug is None, that's the top-level category listing.
    """
    fs_root = config()["cache"]["fs_root"]

    # Build path to listings/.../items.json just like fs_products does
    parts = ["listings", top_slug]
    if sub_slug:
        parts.append(sub_slug)
    parts.append("items.json")

    path = os.path.join(fs_root, *parts)
    if not os.path.exists(path):
        return 0

    try:
        all_slugs = _json(path)
    except Exception:
        return 0

    # Filter out blocked products
    allowed = [
        slug for slug in all_slugs
        if not is_product_blocked(slug)
    ]
    return len(allowed)


def fs_products(
    top_slug: str | None,
    sub_slug: str | None,
    selected_brands: Optional[List[str]] = None,
    selected_stickers: Optional[List[str]] = None,
    selected_labels: Optional[List[str]] = None,
    page: int = 1,
    search: Optional[str] = None,
    sort: Optional[str] = None,
    page_size: int = 20,

    # NEW: only include products the current user has liked
    liked_slugs: Optional[List[str]] = None,
    liked: bool = None,
):
    """
    Returns:
      {
        "total_pages": int,
        "items": [product dict ...],               # filtered + paginated (sorted)
        "brands": [{"name": str, "count": int}],
        "stickers": [{"name": str, "count": int}],
        "labels": [{"name": str, "count": int}],
      }

    Filters:
    - top_slug / sub_slug scope
    - selected_brands
    - selected_stickers
    - selected_labels
    - search
    - liked_slugs (if provided)
    """

    import os
    from typing import List, Dict

    fs_root = config()["cache"]["fs_root"]
    
    # ---------- Collect slugs ----------
    slugs: List[str] = []
    if top_slug:  # normal listing path
        parts = ["listings", top_slug]
        if sub_slug:
            parts.append(sub_slug)
        parts.append("items.json")
        path = os.path.join(fs_root, *parts)
        if os.path.exists(path):
            try:
                slugs = [s for s in _json(path) if not is_product_blocked(s)]
            except Exception:
                slugs = []
    else:
        # No top slug: include ALL products from /products/*.json
        products_dir = os.path.join(fs_root, "products")
        try:
            for fname in os.listdir(products_dir):
                if not fname.endswith(".json"):
                    continue
                slug = fname[:-5]  # strip .json
                if not is_product_blocked(slug):
                    slugs.append(slug)
        except FileNotFoundError:
            slugs = []

    # ---------- Load product dicts ----------
    all_items: List[dict] = []
    for slug in slugs:
        try:
            item = fs_product_by_slug(slug)
            if isinstance(item, dict):
                all_items.append(item)
        except Exception:
            continue

    # Stable deterministic ordering when aggregating everything (name ASC)
    def _title_key(it: dict) -> tuple:
        title = (it.get("title") or it.get("name") or it.get("slug") or "").strip().lower()
        return (title, it.get("slug") or "")

    all_items.sort(key=_title_key)

    # ---------- Helpers for filters & counts ----------
    def _brand_of_local(item: dict) -> str:
        b = item.get("brand") or (item.get("info_table") or {}).get("Brand")
        return (b or "").strip()

    def _stickers_of_local(item: dict) -> List[str]:
        vals = item.get("stickers") or []
        out = []
        for s in vals:
            if isinstance(s, str):
                s2 = s.strip().lower()
                if s2:
                    out.append(s2)
        return out

    def _labels_of_local(item: dict) -> List[str]:
        vals = item.get("labels") or []
        out = []
        for s in vals:
            if isinstance(s, str):
                s2 = s.strip().lower()
                if s2:
                    out.append(s2)
        return out

    sel_brands = [
        (s or "").strip().lower()
        for s in (selected_brands or [])
        if (s or "").strip()
    ]
    sel_stickers = [
        (s or "").strip().lower()
        for s in (selected_stickers or [])
        if (s or "").strip()
    ]
    sel_labels = [
        (s or "").strip().lower()
        for s in (selected_labels or [])
        if (s or "").strip()
    ]
    search_q = (search or "").strip().lower() or None

    liked_set = {
        (slug or "").strip().lower()
        for slug in (liked_slugs or [] if liked else [])
        if (slug or "").strip()
    }

    real_liked_set = {
        (slug or "").strip().lower()
        for slug in (liked_slugs or [])
        if (slug or "").strip()
    }    

    def matches_brand(item: dict) -> bool:
        if not sel_brands:
            return True
        return _brand_of_local(item).strip().lower() in sel_brands

    def has_all_selected_stickers(item: dict) -> bool:
        if not sel_stickers:
            return True
        tags = set(_stickers_of_local(item))
        return all(s in tags for s in sel_stickers)

    def has_all_selected_labels(item: dict) -> bool:
        if not sel_labels:
            return True
        tags = set(_labels_of_local(item))
        return all(s in tags for s in sel_labels)

    def matches_search(item: dict) -> bool:
        if not search_q:
            return True
        desc = (item.get("description_short") or "").strip().lower()
        return search_q in desc

    def is_liked(item: dict) -> bool:
        """
        True if this item should be shown under the liked filter.
        If liked_set is empty, treat everything as allowed.
        """
        slug_val = (item.get("slug") or "").strip().lower()
        return slug_val in real_liked_set

    # ---------- Counts (dependent on other filters + search + liked) ----------
    brand_counts: Dict[str, int] = {}
    for b in (selected_brands or []):
        brand_counts[b] = 0

    for it in all_items:
        b = _brand_of_local(it)
        if not b:
            continue
        brand_counts[b] = brand_counts.get(b, 0) + 1

    sticker_counts: Dict[str, int] = {}
    for s in (selected_stickers or []):
        sticker_counts[s] = 0
    for it in all_items:
        for s in _stickers_of_local(it):
            sticker_counts[s] = sticker_counts.get(s, 0) + 1

    label_counts: Dict[str, int] = {}
    for s in (selected_labels or []):
        label_counts[s] = 0
    for it in all_items:
        for s in _labels_of_local(it):
            label_counts[s] = label_counts.get(s, 0) + 1

    liked_count = 0
    for it in all_items:
      if is_liked(it):
        liked_count += 1

    search_count=0
    for it in all_items:
      if matches_search(it):
        search_count += 1


    # ---------- Apply filters ----------
    filtered = [
        it
        for it in all_items
        if matches_brand(it)
        and has_all_selected_stickers(it)
        and has_all_selected_labels(it)
        and matches_search(it)
        and (not liked or is_liked(it))
    ]

    # ---------- Sorting ----------
    sort_mode = (sort or "az").strip().lower()

    def _price_key(item: dict):
        p = item["regular_price"]
        title, slug = _title_key(item)
        return (0 if p is not None else 1, p if p is not None else 0, title, slug)

    def _price_key_desc(item: dict):
        p = item["regular_price"]
        title, slug = _title_key(item)
        return (
            0 if p is not None else 1,
            -(p if p is not None else 0),
            title,
            slug,
        )

    if sort_mode in ("az",):
        filtered.sort(key=_title_key)
    elif sort_mode in ("za",):
        filtered.sort(key=_title_key, reverse=True)
    elif sort_mode in (
        "price-asc", "price_asc", "price-low", "price-low-high", "low-high", "lo-hi"
    ):
        filtered.sort(key=_price_key)
    elif sort_mode in (
        "price-desc", "price_desc", "price-high", "price-high-low", "high-low", "hi-lo"
    ):
        filtered.sort(key=_price_key_desc)
    else:
        filtered.sort(key=_title_key)

    # ---------- Pagination ----------
    total_pages = max(1, (len(filtered) + page_size - 1) // page_size)
    page = max(1, page)
    start = (page - 1) * page_size
    end = start + page_size
    page_items = filtered[start:end]
    # ---------- Format counts lists ----------
    brands_list = sorted(
        [{"name": k, "count": v} for k, v in brand_counts.items()],
        key=lambda x: (-x["count"], x["name"].lower()),
    )
    stickers_list = sorted(
        [{"name": k, "count": v} for k, v in sticker_counts.items()],
        key=lambda x: (-x["count"], x["name"]),
    )
    labels_list = sorted(
        [{"name": k, "count": v} for k, v in label_counts.items()],
        key=lambda x: (-x["count"], x["name"]),
    )
    return {
        "total_pages": total_pages,
        "items": page_items,
        "brands": brands_list,
        "stickers": stickers_list,
        "labels": labels_list,
        "liked_count": liked_count,
        "search_count": search_count
    }

# async wrappers (unchanged)
async def read_nav():
    return fs_nav()

async def read_listing(top_slug: str, sub_slug: str | None, page: int):
    return fs_products(top_slug, sub_slug, None, None, page)

async def read_product(slug_or_path: str):
    slug = (slug_or_path or "").strip()
    if "/" in slug:
        slug = slug.rsplit("/", 1)[-1]
    slug = slug.split("?", 1)[0]
    return fs_product_by_slug(slug)
