"""
Derive the `size` field the ingestion API requires for every product.

The Ops products:batch endpoint rejects any product with an empty size
("size/weight required for comparison"). None of the scrapers store a clean
size column, so we extract it from the product name, falling back to the
retailer's unit-of-measure / average-weight fields when the name has none.
"""
from __future__ import annotations

import re

# Matches "2L", "700 g", "1,5 kg", "500ml", "18s", "6 pack", "2 x 1L", etc.
_UNIT = r"(?:kg|g|mg|l|ml|cl|s|ea|pack|pk|un|units?|pieces?|sheets?|rolls?|tabs?|caps?)"
_QTY = r"\d+(?:[.,]\d+)?"
_SIZE_RE = re.compile(
    rf"\b(?:\d+\s*[x×]\s*)?{_QTY}\s*{_UNIT}\b",
    re.IGNORECASE,
)
_WS_RE = re.compile(r"\s+")


def _clean(text: str) -> str:
    return _WS_RE.sub(" ", text).strip()


def from_name(name: str | None) -> str | None:
    """Return the last size-like token in the name (usually the pack size)."""
    if not name:
        return None
    matches = _SIZE_RE.findall(name)
    if not matches:
        return None
    return _clean(matches[-1])


def derive(
    name: str | None,
    unit_of_measure: str | None = None,
    average_weight: float | None = None,
    quantity_type: str | None = None,
) -> str | None:
    """
    Best-effort size for one product.

    Order: explicit size in the name -> unit_of_measure -> average weight.
    Returns None when nothing usable is found (caller then skips the row so it
    is not rejected server-side).
    """
    size = from_name(name)
    if size:
        return size[:48]

    uom = _clean(unit_of_measure or "")
    if uom and uom.lower() not in ("each", "ea", "unit", "units"):
        return uom[:48]

    if average_weight:
        unit = "kg" if (quantity_type or "").lower().startswith("k") else "g"
        return f"{average_weight:g}{unit}"[:48]

    return None
