from __future__ import annotations

from collections import defaultdict
from pathlib import Path
from typing import Any, Dict, Iterable, List, Optional

from db.brochure_pdf_taxonomy import _extract_pdf_text, _extract_sku_tokens
from db.brochure_type_key_taxonomy import taxonomy_targets_for_type_key


def build_brochure_sku_vocabulary(pdf_paths: Iterable[str | Path]) -> Dict[str, Any]:
    aggregated: Dict[str, Dict[str, Any]] = {}
    source_pdf_count = 0

    for pdf_path in pdf_paths:
        path = Path(pdf_path)
        if not path.is_file():
            continue
        source_pdf_count += 1
        for sku in _extract_sku_tokens(_extract_pdf_text(path)):
            variant = classify_brochure_sku_variant(sku)
            if variant is None:
                continue
            type_key = variant["type_key"]
            row = aggregated.setdefault(
                type_key,
                {
                    "type_key": type_key,
                    "brochure_l1_label": variant["brochure_l1_label"],
                    "brochure_l2_label": variant.get("brochure_l2_label"),
                    "type_label": variant["type_label"],
                    "package_template": variant.get("package_template"),
                    "sku_template": variant.get("sku_template"),
                    "sku_examples": [],
                    "variants": [],
                    "source_pdfs": [],
                    **taxonomy_targets_for_type_key(type_key),
                },
            )
            if path.name not in row["source_pdfs"]:
                row["source_pdfs"].append(path.name)
            if sku not in row["sku_examples"]:
                row["sku_examples"].append(sku)
            variant_record = {
                "sku": sku,
                "label": variant["label"],
            }
            dimensions = variant.get("dimensions")
            if dimensions:
                variant_record["dimensions"] = dimensions
            if variant_record not in row["variants"]:
                row["variants"].append(variant_record)

    types = sorted(
        aggregated.values(),
        key=lambda item: (
            item["brochure_l1_label"],
            str(item.get("brochure_l2_label") or ""),
            item["type_label"],
        ),
    )
    for item in types:
        item["source_pdfs"].sort()
        item["sku_examples"].sort()
        item["variants"] = sorted(item["variants"], key=lambda row: row["label"])

    return {
        "source_pdf_count": source_pdf_count,
        "type_count": len(types),
        "types": types,
    }


def classify_brochure_sku_variant(sku: str) -> Optional[Dict[str, Any]]:
    code = str(sku or "").strip().upper()
    if not code:
        return None

    # Wall cabinets
    if _wall_basic(code):
        width, height = _two_dims(code)
        subtype = "1 Door" if width is not None and width <= 21 else "2 Door"
        return _entry(
            type_key=f"wall.basic.{subtype.lower().replace(' ', '-')}",
            brochure_l1="Wall Cabinets",
            brochure_l2="Basic Wall",
            type_label=f"Basic Wall - {subtype}",
            label=f"Wall {width} x {height}" if width and height else code,
            sku_template="W{width}{height}",
            package_template="Wall {width} x {height}",
            dimensions=_dims(width=width, height=height),
        )
    if _wall_basic_glass(code):
        width, height = _two_dims(code)
        subtype = "1 Door Glass" if width is not None and width <= 21 else "2 Door Glass"
        return _entry(
            type_key=f"wall.basic-glass.{subtype.lower().replace(' ', '-')}",
            brochure_l1="Wall Cabinets",
            brochure_l2="Basic Wall",
            type_label=f"Basic Wall - {subtype}",
            label=f"Glass Door Wall {width} x {height}" if width and height else code,
            sku_template="GDW{width}{height}",
            package_template=None,
            dimensions=_dims(width=width, height=height),
        )
    if _bridge_wall(code):
        width, height = _bridge_dims(code)
        return _entry(
            type_key="wall.bridge",
            brochure_l1="Wall Cabinets",
            brochure_l2="Bridge Wall",
            type_label="Bridge Wall",
            label=f"Bridge Wall {width} x {height}" if width and height else code,
            sku_template="W{width}{height}",
            package_template=None,
            dimensions=_dims(width=width, height=height),
        )
    if code.startswith("WCOR"):
        width, height = _suffix_dims(code, prefix="WCOR")
        return _entry(
            type_key="wall.corner.pie-cut",
            brochure_l1="Wall Cabinets",
            brochure_l2="Corner Wall",
            type_label="Pie Cut Corner",
            label=f"Wall Corner {width} x {height}" if width and height else code,
            sku_template="WCOR{width}{height}",
            package_template="Wall Corner {width} x {height}",
            dimensions=_dims(width=width, height=height),
        )
    if code.startswith("WDC"):
        width, height = _suffix_dims(code, prefix="WDC")
        return _entry(
            type_key="wall.corner.diagonal",
            brochure_l1="Wall Cabinets",
            brochure_l2="Corner Wall",
            type_label="Diagonal Corner",
            label=f"Diagonal Corner Wall {width} x {height}" if width and height else code,
            sku_template="WDC{width}{height}",
            package_template=None,
            dimensions=_dims(width=width, height=height),
        )
    if code.startswith("GDWDC"):
        width, height = _suffix_dims(code, prefix="GDWDC")
        return _entry(
            type_key="wall.corner.diagonal-glass",
            brochure_l1="Wall Cabinets",
            brochure_l2="Corner Wall",
            type_label="Diagonal Corner - Glass",
            label=f"Diagonal Corner Glass Wall {width} x {height}" if width and height else code,
            sku_template="GDWDC{width}{height}",
            package_template=None,
            dimensions=_dims(width=width, height=height),
        )
    if code.startswith("WBC"):
        width, height = _suffix_dims(code, prefix="WBC")
        return _entry(
            type_key="wall.corner.blind",
            brochure_l1="Wall Cabinets",
            brochure_l2="Corner Wall",
            type_label="Wall Blind",
            label=f"Wall Blind {width} x {height}" if width and height else code,
            sku_template="WBC{width}{height}",
            package_template=None,
            dimensions=_dims(width=width, height=height),
        )
    if code.startswith(("WECA", "WEC")):
        width = _tail_int(code)
        return _entry(
            type_key="wall.special.angle-end",
            brochure_l1="Wall Cabinets",
            brochure_l2="Special Wall",
            type_label="Angle End",
            label=f"Angle End Wall {width}" if width else code,
            sku_template="WECA{width}",
            package_template=None,
            dimensions=_dims(width=width),
        )
    if code.startswith(("WSC", "WR", "WPR", "WGR")):
        width, height = _two_dims(code)
        return _entry(
            type_key="wall.special.wine-rack",
            brochure_l1="Wall Cabinets",
            brochure_l2="Special Wall",
            type_label="Wine Rack",
            label=f"Wine Rack {width} x {height}" if width and height else code,
            sku_template=None,
            package_template=None,
            dimensions=_dims(width=width, height=height),
        )
    if code.startswith(("MC", "WMC")):
        width, height = _two_dims(code)
        return _entry(
            type_key="wall.special.microwave-shelf",
            brochure_l1="Wall Cabinets",
            brochure_l2="Special Wall",
            type_label="Microwave Shelf",
            label=f"Microwave Shelf {width} x {height}" if width and height else code,
            sku_template="MC{width}{height}",
            package_template=None,
            dimensions=_dims(width=width, height=height),
        )
    if code.startswith(("WHB", "RHAR", "RHCL")):
        width, height = _two_dims(code)
        return _entry(
            type_key="wall.special.wall-hood",
            brochure_l1="Wall Cabinets",
            brochure_l2="Special Wall",
            type_label="Wall Hood",
            label=f"Wall Hood {width} x {height}" if width and height else code,
            sku_template=None,
            package_template=None,
            dimensions=_dims(width=width, height=height),
        )

    # Base cabinets
    if code.startswith("BH"):
        width = _tail_int(code)
        return _entry(
            type_key="base.basic.full-height-door",
            brochure_l1="Base Cabinets",
            brochure_l2="Basic Bases",
            type_label="Full Height Door",
            label=f"Base Full Height {width}" if width else code,
            sku_template="BH{width}",
            package_template=None,
            dimensions=_dims(width=width),
        )
    if _base_basic(code):
        width = _tail_int(code)
        subtype = "1 Door, 1 Drawer" if width is not None and width <= 21 else "2 Door, 1 Drawer"
        return _entry(
            type_key=f"base.basic.{subtype.lower().replace(', ', '-').replace(' ', '-')}",
            brochure_l1="Base Cabinets",
            brochure_l2="Basic Bases",
            type_label=subtype,
            label=f"Base {width}" if width else code,
            sku_template="B{width}",
            package_template="Base {width}",
            dimensions=_dims(width=width),
        )
    if code.startswith("SB") and not code.startswith("SBF"):
        width = _tail_int(code)
        return _entry(
            type_key="base.sink.sink-base",
            brochure_l1="Base Cabinets",
            brochure_l2="Sink Bases",
            type_label="Sink Base",
            label=f"Base Sink {width}" if width else code,
            sku_template="SB{width}",
            package_template="Base Sink {width}",
            dimensions=_dims(width=width),
        )
    if code.startswith("SBF"):
        width = _tail_int(code)
        return _entry(
            type_key="base.sink.farm-sink-base",
            brochure_l1="Base Cabinets",
            brochure_l2="Sink Bases",
            type_label="Farm Sink Base",
            label=f"Farm Sink Base {width}" if width else code,
            sku_template="SBF{width}",
            package_template=None,
            dimensions=_dims(width=width),
        )
    if code.startswith("CSB") and code.endswith("-FP"):
        width = _first_number(code)
        return _entry(
            type_key="base.sink.corner-sink-base",
            brochure_l1="Base Cabinets",
            brochure_l2="Sink Bases",
            type_label="Corner Sink Base",
            label=f"Corner Sink Base {width}" if width else code,
            sku_template="CSB{width}-FP",
            package_template=None,
            dimensions=_dims(width=width),
        )
    if code.startswith("CSB"):
        width = _first_number(code)
        return _entry(
            type_key="base.drawer.corner-sink-front",
            brochure_l1="Base Cabinets",
            brochure_l2="Drawer Bases",
            type_label="Corner Sink Front",
            label=f"Corner Sink Front {width}" if width else code,
            sku_template="CSB{width}",
            package_template=None,
            dimensions=_dims(width=width),
        )
    if code.startswith("DB") and code.endswith("-2"):
        width = _first_number(code)
        return _entry(
            type_key="base.drawer.2-drawer-base",
            brochure_l1="Base Cabinets",
            brochure_l2="Drawer Bases",
            type_label="2 Drawer Base",
            label=f"2 Drawer Base {width}" if width else code,
            sku_template="DB{width}-2",
            package_template=None,
            dimensions=_dims(width=width),
        )
    if code.startswith("DB") and code.endswith("-3"):
        width = _first_number(code)
        return _entry(
            type_key="base.drawer.3-drawer-base",
            brochure_l1="Base Cabinets",
            brochure_l2="Drawer Bases",
            type_label="3 Drawer Base",
            label=f"3 Drawer Base {width}" if width else code,
            sku_template="DB{width}-3",
            package_template=None,
            dimensions=_dims(width=width),
        )
    if code.startswith(("BCOR", "LS", "PBWLS")):
        width = _first_number(code)
        return _entry(
            type_key="base.corner.lazy-susan",
            brochure_l1="Base Cabinets",
            brochure_l2="Corner Bases",
            type_label="Lazy Susan",
            label=f"Lazy Susan {width}" if width else code,
            sku_template="BCOR{width}L/R",
            package_template="Lazy Susan {width}",
            dimensions=_dims(width=width),
        )
    if code.startswith(("EZR", "EZ")):
        width = _tail_int(code)
        return _entry(
            type_key="base.corner.ez-reach",
            brochure_l1="Base Cabinets",
            brochure_l2="Corner Bases",
            type_label="EZ Reach",
            label=f"EZ Reach {width}" if width else code,
            sku_template="EZR{width}",
            package_template=None,
            dimensions=_dims(width=width),
        )
    if code.startswith("BBC"):
        first, second = _bbc_widths(code)
        package_width = second or first
        return _entry(
            type_key="base.corner.blind-corner",
            brochure_l1="Base Cabinets",
            brochure_l2="Corner Bases",
            type_label="Blind Corner",
            label=f"Blind Base {package_width}" if package_width else code,
            sku_template="BBC{width}/{alt_width}L/R",
            package_template="Blind Base {width}",
            dimensions=_dims(width=package_width, alt_width=first),
        )
    if code.startswith("PC"):
        width, height = _two_dims(code)
        subtype = "2 Door Pantry" if width == 18 else "4 Door Pantry"
        return _entry(
            type_key=f"base.tall.{subtype.lower().replace(' ', '-')}",
            brochure_l1="Base Cabinets",
            brochure_l2="Tall Cabinets",
            type_label=subtype,
            label=f"Pantry {width} x {height}" if width and height else code,
            sku_template="PC{width}{height}",
            package_template="Pantry {width} x {height}",
            dimensions=_dims(width=width, height=height),
        )
    if code.startswith("OC"):
        width, height = _two_dims(code)
        return _entry(
            type_key="base.tall.single-oven",
            brochure_l1="Base Cabinets",
            brochure_l2="Tall Cabinets",
            type_label="Single Oven",
            label=f"Oven {width} x {height}" if width and height else code,
            sku_template="OC{width}{height}",
            package_template="Oven {width} x {height}",
            dimensions=_dims(width=width, height=height),
        )
    if code.startswith("BEC"):
        width, height = _two_dims(code)
        return _entry(
            type_key="base.special.angle-end",
            brochure_l1="Base Cabinets",
            brochure_l2="Special Bases",
            type_label="Angle End",
            label=f"Angle End Base {width} x {height}" if width and height else code,
            sku_template="BEC{width}{height}L/R",
            package_template=None,
            dimensions=_dims(width=width, height=height),
        )
    if code.startswith("BMC"):
        width = _tail_int(code)
        return _entry(
            type_key="base.special.microwave-base",
            brochure_l1="Base Cabinets",
            brochure_l2="Special Bases",
            type_label="Microwave Base",
            label=f"Microwave Base {width}" if width else code,
            sku_template="BMC{width}",
            package_template=None,
            dimensions=_dims(width=width),
        )
    if code.startswith("BWB"):
        width = _tail_int(code)
        return _entry(
            type_key="base.special.trash-pullout",
            brochure_l1="Base Cabinets",
            brochure_l2="Special Bases",
            type_label="Trash Pullout",
            label=f"Trash Pullout {width}" if width else code,
            sku_template="BWB{width}",
            package_template=None,
            dimensions=_dims(width=width),
        )

    # Panels / mouldings / accessories
    if code.startswith("BF"):
        width = _tail_int(code)
        return _entry("misc.panels.base-filler", "Panels & Fillers", None, "Base Filler", f"Base Filler {width}" if width else code)
    if code.startswith("F") and not code.startswith(("FS", "FPP")):
        width = _tail_int(code)
        return _entry("misc.panels.filler", "Panels & Fillers", None, "Filler", f"Filler {width}" if width else code)
    if code.startswith(("MBEP", "MWEP", "MPBEP")):
        return _entry("misc.panels.deco-panel", "Panels & Fillers", None, "Deco Panel", code)
    if code.startswith(("FPP", "DWR", "REP")):
        return _entry("misc.panels.panels", "Panels & Fillers", None, "Panels", code)
    if code == "SD":
        return _entry("misc.panels.sample-door", "Panels & Fillers", None, "Sample Door", "Sample Door")
    if code.startswith("OCM"):
        return _entry("misc.mouldings.outside-corner", "Moldings", None, "Outside Corner", code)
    if code.startswith("SBM"):
        return _entry("misc.mouldings.single-bead", "Moldings", None, "Single Bead", code)
    if code.startswith("TKBM"):
        return _entry("misc.mouldings.furniture-base", "Moldings", None, "Furniture Base", code)
    if code.startswith("LRM"):
        return _entry("misc.mouldings.light-rail", "Moldings", None, "Light Rail", code)
    if code.startswith("TK"):
        return _entry("misc.mouldings.toe-kick", "Moldings", None, "Toe Kick", code)
    if code.startswith("SHM"):
        return _entry("misc.mouldings.shoe", "Moldings", None, "Shoe", code)
    if code.startswith("SM"):
        return _entry("misc.mouldings.scribe", "Moldings", None, "Scribe", code)
    if code.startswith("TLM"):
        return _entry("misc.mouldings.large-crown", "Moldings", None, "Large Crown", code)
    if code.startswith(("CVM", "LCVM")):
        return _entry("misc.mouldings.cove-crown", "Moldings", None, "Cove Crown", code)
    if code.startswith("GD-W"):
        width, height = _two_dims(code)
        return _entry("misc.accessories.glass-door", "Accessories", None, "Glass Door", f"Glass Door {width} x {height}" if width and height else code)
    if code.startswith("ROTD"):
        width = _tail_int(code)
        return _entry("misc.accessories.rollout-tray", "Accessories", None, "Rollout Tray", f"Rollout Tray {width}" if width else code)
    if code.startswith("V"):
        width = _tail_int(code)
        return _entry("misc.accessories.valance-arch", "Accessories", None, "Valance Arch", f"Valance Arch {width}" if width else code)
    if code.startswith("SC"):
        width = _tail_int(code)
        return _entry("misc.accessories.corbel", "Accessories", None, "Corbel", f"Corbel {width}" if width else code)
    if code.startswith("VKD"):
        width = _tail_int(code)
        return _entry("misc.accessories.knee-drawer", "Accessories", None, '30" W Knee Drawer', f'Knee Drawer {width}' if width else code)
    if code.startswith(("SSPA", "SDP")):
        return _entry("misc.accessories.post", "Accessories", None, "Post", code)
    if code.startswith("FS"):
        width = _tail_int(code)
        return _entry("misc.accessories.floating-shelf", "Accessories", None, "Floating Shelf", f"Floating Shelf {width}" if width else code)

    return None


def _entry(
    type_key: str,
    brochure_l1: str,
    brochure_l2: Optional[str],
    type_label: str,
    label: str,
    *,
    sku_template: Optional[str] = None,
    package_template: Optional[str] = None,
    dimensions: Optional[Dict[str, int]] = None,
) -> Dict[str, Any]:
    out: Dict[str, Any] = {
        "type_key": type_key,
        "brochure_l1_label": brochure_l1,
        "brochure_l2_label": brochure_l2,
        "type_label": type_label,
        "label": label,
    }
    if sku_template:
        out["sku_template"] = sku_template
    if package_template:
        out["package_template"] = package_template
    if dimensions:
        out["dimensions"] = dimensions
    return out


def _dims(**kwargs: Optional[int]) -> Optional[Dict[str, int]]:
    out = {key: int(value) for key, value in kwargs.items() if value is not None}
    return out or None


def _wall_basic(code: str) -> bool:
    return len(code) == 5 and code.startswith("W") and code[1:].isdigit()


def _wall_basic_glass(code: str) -> bool:
    return code.startswith("GDW") and len(code) >= 7 and _two_dims(code) != (None, None)


def _bridge_wall(code: str) -> bool:
    return code.startswith("W") and code[1:].isdigit() and len(code) in {5, 7}


def _base_basic(code: str) -> bool:
    return code.startswith("B") and len(code) == 3 and code[1:].isdigit()


def _two_dims(code: str) -> tuple[Optional[int], Optional[int]]:
    digits = "".join(ch for ch in code if ch.isdigit())
    if len(digits) < 4:
        return None, None
    width = int(digits[:2])
    height = int(digits[2:4])
    return width, height


def _bridge_dims(code: str) -> tuple[Optional[int], Optional[int]]:
    digits = "".join(ch for ch in code if ch.isdigit())
    if len(digits) < 4:
        return None, None
    width = int(digits[:2])
    height = int(digits[2:4])
    return width, height


def _suffix_dims(code: str, *, prefix: str) -> tuple[Optional[int], Optional[int]]:
    if not code.startswith(prefix):
        return None, None
    digits = "".join(ch for ch in code[len(prefix):] if ch.isdigit())
    if len(digits) < 4:
        return None, None
    return int(digits[:2]), int(digits[2:4])


def _tail_int(code: str) -> Optional[int]:
    num = _first_number(code)
    return num


def _first_number(code: str) -> Optional[int]:
    digits = ""
    for ch in code:
        if ch.isdigit():
            digits += ch
        elif digits:
            break
    if not digits:
        return None
    return int(digits)


def _bbc_widths(code: str) -> tuple[Optional[int], Optional[int]]:
    digits = [int(part) for part in "".join(ch if ch.isdigit() else " " for ch in code).split()]
    if not digits:
        return None, None
    if len(digits) == 1:
        return digits[0], None
    return digits[0], digits[1]
