"""Jinja context processors and price display globals."""
from datetime import datetime

from nexora.config import APP_START_TS, BASE_DIR, PUBLIC_CACHE_SECONDS
from nexora.core import app
from nexora.database.connection import cached_query, query
from nexora.utils.helpers import split_upload_list
from nexora.services.pricing import (
    disc_price_filter,
    discount_active,
    discount_label_text,
    discount_pct_value,
    get_discount_type,
    game_price_for_size,
    game_status,
)


def _scan_static_bust():
    """Compute the cache-busting version once at import time.

    The site loads many CSS/JS files. Only versioning three "main" files let edited assets go
    stale in browser memory. This scans everything under static/ and always factors
    in the process start time, so a restart alone also bumps it.
    """
    import os as _os
    cache_bust = APP_START_TS
    try:
        root = BASE_DIR / "static"
        stack = [root / "css", root / "js"]
        while stack:
            folder = stack.pop()
            if not folder.is_dir():
                continue
            with _os.scandir(str(folder)) as entries:
                for entry in entries:
                    try:
                        if entry.is_dir(follow_symlinks=False):
                            stack.append(folder / entry.name)
                        elif entry.is_file(follow_symlinks=False):
                            ext = _os.path.splitext(entry.name)[1].lower()
                            if ext in (".css", ".js"):
                                cache_bust = max(cache_bust, int(entry.stat().st_mtime))
                    except OSError:
                        pass
    except OSError:
        pass
    return cache_bust


# Computed once per process; no per-request filesystem scanning.
_STATIC_BUST = _scan_static_bust()


def _static_bust():
    """Return the precomputed cache-busting version."""
    return _STATIC_BUST


@app.context_processor
def inject_common():
    settings = cached_query("settings", "SELECT * FROM settings WHERE id = 1", one=True, ttl=PUBLIC_CACHE_SECONDS)
    prize_banner_images = []
    if settings:
        prize_banner_images = split_upload_list(settings["prize_banner_images"] if "prize_banner_images" in settings.keys() else "")
        if not prize_banner_images and settings["prize_banner_image"]:
            prize_banner_images = [settings["prize_banner_image"]]
    cache_bust = _static_bust()
    return {
        "settings": settings,
        "current_year": datetime.now().year,
        "game_status": game_status,
        "discount_active": discount_active,
        "discount_pct_value": discount_pct_value,
        "get_discount_type": get_discount_type,
        "discount_label_text": discount_label_text,
        "prize_banner_images": prize_banner_images,
        "prize_items": cached_query("active_prize_items", "SELECT * FROM prize_items WHERE active = 1 ORDER BY sort_order, id", ttl=PUBLIC_CACHE_SECONDS),
        "cache_bust": cache_bust,
    }


@app.template_global("price_view")
def price_view(game, size=1):
    """Single source of truth for public price display.

    Returns None when the game has no configured price for this size,
    otherwise {"current", "original", "strike"}:
      - discount ACTIVE  -> current = discounted, original = regular, strike = True
      - discount EXPIRED -> current = regular,    original = "",       strike = False
    Uses the exact same helpers as registration pricing, so the UI price
    always matches what the player is actually charged.
    """
    base = game_price_for_size(game, size)
    if not base:
        return None
    active = bool(game and discount_active(game))
    pct = discount_pct_value(game) if active else 0
    dtype = get_discount_type(game)
    result = disc_price_filter(base, pct, dtype)
    if result["active"]:
        return {"current": result["discounted"], "original": result["original"], "strike": True}
    return {"current": result["original"], "original": "", "strike": False}


@app.template_global("price_map")
def price_map(game):
    """Backend-precomputed prices for every supported team size.

    Returns {size_string: {"current", "original", "strike"}} for sizes that
    have a configured price. Templates embed this as JSON on <option> tags so
    the frontend never recalculates discounts itself.
    """
    gm = dict(game) if hasattr(game, "keys") else game
    try:
        gm_max = int(gm.get("max_members") or 4)
    except (TypeError, ValueError):
        gm_max = 4
    out = {}
    for size in range(1, min(gm_max, 4) + 1):
        view = price_view(game, size)
        if view:
            out[str(size)] = view
    return out


@app.template_global("qualifier_card_prices")
def qualifier_card_prices(game):
    """Price lines for a qualifier-enabled game card.

    - package fee_type -> single package fee line with discount (solo style:
      old price struck / new price after discount).
    - separate fee_type -> one line per qualifier (Q1..Q4) with its fee.
    - Returns [] when the game has no qualifiers enabled or no fees set.
    """
    if not game:
        return []
    if "qualifiers_enabled" not in game.keys() or str(game["qualifiers_enabled"]) != "1":
        return []
    gm = dict(game) if hasattr(game, "keys") else game
    pct = discount_pct_value(gm) if discount_active(gm) else 0
    dtype = get_discount_type(gm)
    fee_type = str(gm.get("qualifier_fee_type") or "separate").lower()

    def line(label, meta, raw):
        d = disc_price_filter(raw, pct, dtype)
        return {
            "label": label,
            "meta": meta,
            "now": d["discounted"],
            "original": d["original"] if d["active"] else "",
            "strike": d["active"],
        }

    if fee_type == "package":
        raw = str(gm.get("qualifier_package_fee") or "").strip()
        if not raw:
            return []
        pkg_size = int(gm.get("qualifier_package_size") or 1)
        meta = f"{pkg_size} Qualifier{'s' if pkg_size != 1 else ''} Included"
        return [line("Package", meta, raw)]

    quals = query(
        "SELECT * FROM qualifiers WHERE game_id = ? ORDER BY qualifier_number, sort_order, id",
        (gm.get("id"),),
    )
    lines = []
    for q in quals:
        raw = str(q["fee"] or "").strip()
        if not raw:
            continue
        num = q["qualifier_number"] or q["id"]
        name = q["qualifier_name"] or ""
        lines.append(line(f"Q{num}", name, raw))
    return lines
