#!/usr/bin/env python3
"""Voucher normal-price consensus loader/cache.

Extracted from `Inventory_Search_Folder.voucher_search` so web/desktop callers
can reuse the same logic from a standalone `Boot_Features` module.
"""

from __future__ import annotations

from collections import Counter, defaultdict
import json
import os
from pathlib import Path
import re
from typing import Dict, List, Optional, Tuple


_VOUCHER_PRICE_LINE_RE = re.compile(
    r"^\s*(?P<style>\d{4,6})\s*,.*?,\s*"
    r"(?P<normal>\d+(?:\.\d{2})?)\s*,,\s*"
    r"(?P<discount>\d+(?:\.\d{1,2})?)\s*,\s*"
    r"(?P<discounted>\d+(?:\.\d{2})?)\b"
)


def _price_consensus_cache_path(voucher_root: Path) -> Path:
    try:
        cfg = Path(voucher_root).resolve().parent / "config"
        return (cfg / "voucher_price_consensus_cache.json").resolve()
    except Exception:
        return (Path(voucher_root).resolve() / ".voucher_price_consensus_cache.json").resolve()


def _compute_voucher_dir_stamp(voucher_root: Path) -> List[Tuple[str, int]]:
    out: List[Tuple[str, int]] = []
    root = Path(voucher_root).expanduser().resolve()
    if not root.exists():
        return out
    try:
        for dirpath, dirnames, _ in os.walk(root):
            try:
                p = Path(dirpath)
                rel = "." if p == root else str(p.relative_to(root))
                st = p.stat()
                mtime_ns = int(getattr(st, "st_mtime_ns", int(st.st_mtime * 1_000_000_000)))
                out.append((rel, mtime_ns))
                dirnames.sort(key=str.casefold)
            except Exception:
                continue
    except Exception:
        return []
    out.sort(key=lambda t: t[0].casefold())
    return out


def load_voucher_normal_price_consensus(
    voucher_root: Path,
    *,
    normalize_style,
    valid_styles: Optional[set[str]] = None,
    min_agree: int = 2,
    force_rebuild: bool = False,
) -> Dict[str, float]:
    """Build/load cached normal-price consensus from voucher `.md` files."""
    root = Path(voucher_root).expanduser().resolve()
    cache_path = _price_consensus_cache_path(root)
    current_stamp = _compute_voucher_dir_stamp(root)

    if not force_rebuild:
        try:
            if cache_path.is_file():
                payload = json.loads(cache_path.read_text(encoding="utf-8"))
                if (
                    isinstance(payload, dict)
                    and int(payload.get("version", 0)) == 1
                    and str(payload.get("root", "")) == str(root)
                    and payload.get("dir_stamp") == [[a, b] for a, b in current_stamp]
                    and isinstance(payload.get("prices"), dict)
                ):
                    return {
                        str(k): float(v)
                        for k, v in payload.get("prices", {}).items()
                        if str(k) and float(v or 0) > 0
                    }
        except Exception:
            pass

    style_price_counts: Dict[str, Counter[float]] = defaultdict(Counter)

    try:
        md_files = sorted(root.rglob("*.md"))
    except Exception:
        md_files = []
    for p in md_files:
        try:
            file_seen: Dict[str, float] = {}
            with p.open("r", encoding="utf-8", errors="ignore") as f:
                for ln in f:
                    m = _VOUCHER_PRICE_LINE_RE.match(ln or "")
                    if not m:
                        continue
                    norm = normalize_style(m.group("style"))
                    if valid_styles is not None and norm not in valid_styles:
                        continue
                    try:
                        normal_price = float(m.group("normal"))
                    except Exception:
                        continue
                    if normal_price <= 0:
                        continue
                    # Count at most one normal price per style per voucher file.
                    if norm not in file_seen:
                        file_seen[norm] = normal_price
            for norm, normal_price in file_seen.items():
                style_price_counts[norm][normal_price] += 1
        except Exception:
            continue

    prices: Dict[str, float] = {}
    for norm, counter in style_price_counts.items():
        if not counter:
            continue
        ranked = counter.most_common(2)
        top_price, top_count = ranked[0]
        second_count = ranked[1][1] if len(ranked) > 1 else 0
        if top_count >= int(min_agree) and top_count > second_count:
            prices[str(norm)] = float(top_price)

    try:
        cache_path.parent.mkdir(parents=True, exist_ok=True)
        payload = {
            "version": 1,
            "root": str(root),
            "dir_stamp": [[a, b] for a, b in current_stamp],
            "prices": prices,
        }
        cache_path.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8")
    except Exception:
        pass
    return prices


__all__ = ["load_voucher_normal_price_consensus"]
