from __future__ import annotations

import json
import os
import re
from functools import lru_cache
from pathlib import Path
from typing import Any, Dict, Iterable, List, Optional


DEFAULT_VOCAB_PATHS = (
    Path(r"E:\2026\plytixmage_dbs_data\audit_reports\brochure_sku_vocabulary.json"),
    Path(__file__).resolve().parents[1] / "tmp" / "brochure_sku_vocabulary.json",
)


def resolve_brochure_sku_from_description(
    description: str,
    *,
    vocabulary_path: Optional[str | Path] = None,
) -> Optional[str]:
    normalized = normalize_brochure_item_description(description)
    if not normalized:
        return None

    for variant in _all_vocabulary_variants(vocabulary_path=vocabulary_path):
        for candidate in _variant_match_labels(variant):
            if normalized == candidate:
                return str(variant.get("sku") or "").strip() or None

    return _fallback_brochure_sku_from_description(normalized)


def normalize_brochure_item_description(description: str) -> str:
    text = str(description or "").strip().lower()
    if not text:
        return ""
    text = text.replace('"', "")
    text = text.replace("”", "").replace("“", "")
    text = text.replace("′", "'").replace("″", "")
    text = text.replace("-", " ")
    text = re.sub(r"\s+", " ", text)
    return text.strip()


def brochure_sku_vocabulary_path(explicit_path: Optional[str | Path] = None) -> Optional[Path]:
    if explicit_path:
        path = Path(explicit_path)
        return path if path.is_file() else None
    env_path = os.getenv("BROCHURE_SKU_VOCAB_PATH", "").strip()
    if env_path:
        path = Path(env_path)
        return path if path.is_file() else None
    for candidate in DEFAULT_VOCAB_PATHS:
        if candidate.is_file():
            return candidate
    return None


@lru_cache(maxsize=4)
def _load_vocabulary(path_text: str) -> Dict[str, Any]:
    return json.loads(Path(path_text).read_text(encoding="utf-8"))


def _all_vocabulary_variants(*, vocabulary_path: Optional[str | Path]) -> List[Dict[str, Any]]:
    path = brochure_sku_vocabulary_path(vocabulary_path)
    if path is None:
        return []
    payload = _load_vocabulary(str(path))
    variants: List[Dict[str, Any]] = []
    for item in payload.get("types") or []:
        if not isinstance(item, dict):
            continue
        for variant in item.get("variants") or []:
            if not isinstance(variant, dict):
                continue
            merged = dict(variant)
            merged["_type"] = item
            variants.append(merged)
    return variants


def _variant_match_labels(variant: Dict[str, Any]) -> Iterable[str]:
    out: List[str] = []
    label = normalize_brochure_item_description(variant.get("label") or "")
    if label:
        out.append(label)

    type_row = variant.get("_type") if isinstance(variant.get("_type"), dict) else {}
    package_template = str(type_row.get("package_template") or "").strip()
    dimensions = variant.get("dimensions") if isinstance(variant.get("dimensions"), dict) else {}
    if package_template and dimensions:
        rendered = package_template
        for key, value in dimensions.items():
            rendered = rendered.replace("{" + str(key) + "}", str(value))
        rendered_norm = normalize_brochure_item_description(rendered)
        if rendered_norm:
            out.append(rendered_norm)

    type_label = normalize_brochure_item_description(type_row.get("type_label") or "")
    if type_label:
        out.append(type_label)
    return [candidate for candidate in dict.fromkeys(out) if candidate]


def _fallback_brochure_sku_from_description(normalized: str) -> Optional[str]:
    direct = {
        "lazy susan 36": "BCOR36L/R",
        "base sink 36": "SB36",
        "base sink 30": "SB30",
        "base 12": "B12",
        "base 18": "B18",
        "base 24": "B24",
        "wall 12 x 30": "W1230",
        "wall 18 x 30": "W1830",
        "wall 24 x 30": "W2430",
        "wall 30 x 15": "W3015",
        "wall 36 x 15": "W3615",
        "wall corner 24 x 30": "WCOR2430",
        "pantry 18 x 84": "PC1884",
        "oven 30 x 84": "OC3084",
        "blind base 42": "BBC39/42L/R",
    }
    if normalized in direct:
        return direct[normalized]

    match = re.match(r"^wall\s+(\d+)\s*x\s*(\d+)$", normalized)
    if match:
        return f"W{int(match.group(1))}{int(match.group(2))}"

    match = re.match(r"^wall corner\s+(\d+)\s*x\s*(\d+)$", normalized)
    if match:
        return f"WCOR{int(match.group(1))}{int(match.group(2))}"

    match = re.match(r"^base sink\s+(\d+)$", normalized)
    if match:
        return f"SB{int(match.group(1))}"

    match = re.match(r"^base\s+(\d+)$", normalized)
    if match:
        return f"B{int(match.group(1))}"

    match = re.match(r"^pantry\s+(\d+)\s*x\s*(\d+)$", normalized)
    if match:
        return f"PC{int(match.group(1))}{int(match.group(2))}"

    match = re.match(r"^oven\s+(\d+)\s*x\s*(\d+)$", normalized)
    if match:
        return f"OC{int(match.group(1))}{int(match.group(2))}"

    match = re.match(r"^blind base\s+(\d+)$", normalized)
    if match:
        width = int(match.group(1))
        if width <= 39:
            return "BBC36/39L/R"
        if width <= 42:
            return "BBC39/42L/R"
        if width <= 48:
            return "BBC45/48L/R"
        return "BBC45/48FHL/R"

    return None
