# precheck/registry.py
from dataclasses import dataclass, field
from typing import Callable, Optional, Any, List, Dict
from .extractors.weights import extract_preferred_weight_kg, extract_quantities_from_text
from .extractors.dimensions import extract_dimensions_from_text, extract_vehicle_dimensions_from_text
from .extractors.containers import extract_container_mentions
from .extractors.dangerous import extract_dangerous_phrases

@dataclass
class FieldDefinition:
    name: str                             # ключ в JSON shipment
    label: str                            # человекочитаемое название
    type: str                             # "string", "number", "boolean", "array", "object"
    required_by_default: bool = True      # обязательно для любого типа перевозки?
    category: str = "general"             # для группировки в промпте
    extractor: Optional[Callable[[str, Optional[Dict]], Any]] = None  # функция(text, shipment=None) -> Any
    validator: Optional[Callable[[Any], bool]] = None
    dependencies: List[str] = field(default_factory=list)  # имена полей, от которых зависит
    default_value: Any = None             # значение по умолчанию, если не найдено и не обязательно

def _is_non_empty_string(val: Any) -> bool:
    return isinstance(val, str) and bool(val.strip())

def _is_positive_number(val: Any) -> bool:
    if val is None:
        return False
    if isinstance(val, (int, float)):
        return val > 0
    return False

def _is_valid_dimensions(val: Any) -> bool:
    if not isinstance(val, list):
        return False
    for d in val:
        if not isinstance(d, dict):
            return False
        if not all(isinstance(d.get(k), (int, float)) and d.get(k, 0) > 0 for k in ("length_cm", "width_cm", "height_cm")):
            return False
    return True

def _is_valid_dangerous_goods(val: Any) -> bool:
    if not isinstance(val, dict):
        return False
    for k in ("batteries", "gases", "liquids", "dry_ice"):
        if k in val and val[k] not in (True, False, None):
            return False
    return True

# Простые экстракторы-обёртки для использования в реестре
def _extract_total_weight(text: str, shipment: Optional[Dict] = None) -> Optional[float]:
    res = extract_preferred_weight_kg(text, shipment)
    return res.get("value") if res and res.get("value") else None

def _extract_package_count(text: str, shipment: Optional[Dict] = None) -> Optional[int]:
    quant = extract_quantities_from_text(text)
    # берём из отдельной суммы или триплетов
    cnt = quant.get("separate_sum", {}).get("package_count")
    if cnt is None and quant.get("triplet_sum"):
        cnt = quant["triplet_sum"].get("package_count")
    return int(cnt) if cnt is not None else None

def _extract_total_volume(text: str, shipment: Optional[Dict] = None) -> Optional[float]:
    quant = extract_quantities_from_text(text)
    vol = quant.get("separate_sum", {}).get("total_volume_cbm")
    if vol is None and quant.get("triplet_sum"):
        vol = quant["triplet_sum"].get("total_volume_cbm")
    return vol

def _extract_dimensions(text: str, shipment: Optional[Dict] = None) -> Optional[List[Dict]]:
    dims = extract_dimensions_from_text(text)
    return dims if dims else None

def _extract_vehicle_dimensions(text: str, shipment: Optional[Dict] = None) -> Optional[List[Dict]]:
    vdims = extract_vehicle_dimensions_from_text(text)
    return vdims if vdims else None

def _extract_dangerous_goods(text: str, shipment: Optional[Dict] = None) -> Optional[Dict]:
    phrases = extract_dangerous_phrases(text)
    if not phrases:
        return None
    # грубая классификация по ключевым словам
    result = {"batteries": None, "gases": None, "liquids": None, "dry_ice": None}
    text_low = text.lower()
    if any(w in text_low for w in ["батаре", "batter", "lithium"]):
        result["batteries"] = True
    if any(w in text_low for w in ["газ", "gas", "пропан", "бутан"]):
        result["gases"] = True
    if any(w in text_low for w in ["жидкост", "liquid", "растворител", "краск"]):
        result["liquids"] = True
    if any(w in text_low for w in ["сухой лёд", "dry ice"]):
        result["dry_ice"] = True
    return result

# Реестр всех полей
FIELD_REGISTRY: List[FieldDefinition] = [
    FieldDefinition(
        name="client_name",
        label="Клиент",
        type="string",
        required_by_default=True,
        category="general",
        extractor=None,  # пока нет надёжного экстрактора, оставим None
        validator=_is_non_empty_string
    ),
    FieldDefinition(
        name="incoterms",
        label="Условия поставки Incoterms",
        type="string",
        required_by_default=False,
        category="general",
        extractor=None,
    ),
    FieldDefinition(
        name="cargo_ready_date",
        label="Дата готовности груза",
        type="string",
        required_by_default=False,
        category="general",
        extractor=None,
    ),
    FieldDefinition(
        name="pickup_address",
        label="Адрес забора груза",
        type="string",
        required_by_default=True,
        category="route",
        extractor=None,
    ),
    FieldDefinition(
        name="delivery_address",
        label="Адрес доставки",
        type="string",
        required_by_default=True,
        category="route",
        extractor=None,
    ),
    FieldDefinition(
        name="cargo_value",
        label="Стоимость груза",
        type="string",
        required_by_default=False,
        category="general",
        extractor=None,
    ),
    FieldDefinition(
        name="package_count",
        label="Количество грузовых мест",
        type="number",
        required_by_default=True,
        category="cargo",
        extractor=_extract_package_count,
        validator=_is_positive_number,
    ),
    FieldDefinition(
        name="total_weight_kg",
        label="Вес груза (кг)",
        type="number",
        required_by_default=True,
        category="cargo",
        extractor=_extract_total_weight,
        validator=_is_positive_number,
    ),
    FieldDefinition(
        name="dimensions",
        label="Габариты грузовых мест",
        type="array",
        required_by_default=False,
        category="cargo",
        extractor=_extract_dimensions,
        validator=_is_valid_dimensions,
    ),
    FieldDefinition(
        name="total_volume_cbm",
        label="Общий объём (м³)",
        type="number",
        required_by_default=False,
        category="cargo",
        extractor=_extract_total_volume,
        validator=_is_positive_number,
        dependencies=["dimensions"],  # может быть вычислен из размеров
    ),
    FieldDefinition(
        name="cargo_description",
        label="Характер груза",
        type="string",
        required_by_default=True,
        category="cargo",
        extractor=None,
    ),
    FieldDefinition(
        name="hs_code",
        label="Код ТН ВЭД",
        type="string",
        required_by_default=False,
        category="cargo",
        extractor=None,
    ),
    FieldDefinition(
        name="dangerous_goods",
        label="Опасные свойства",
        type="object",
        required_by_default=False,
        category="cargo",
        extractor=_extract_dangerous_goods,
        validator=_is_valid_dangerous_goods,
    ),
    FieldDefinition(
        name="msds_required",
        label="Требуется MSDS",
        type="boolean",
        required_by_default=False,
        category="documents",
        extractor=None,
    ),
    FieldDefinition(
        name="dgm_report_required",
        label="Требуется DGM",
        type="boolean",
        required_by_default=False,
        category="documents",
        extractor=None,
    ),
    FieldDefinition(
        name="brand_name",
        label="Бренд",
        type="string",
        required_by_default=False,
        category="general",
        extractor=None,
    ),
    FieldDefinition(
        name="container_type",
        label="Тип контейнера",
        type="string",
        required_by_default=False,
        category="transport",
        extractor=lambda text, sh: ", ".join(extract_container_mentions(text)) if extract_container_mentions(text) else None,
    ),
    FieldDefinition(
        name="vehicle_type",
        label="Тип транспорта",
        type="string",
        required_by_default=False,
        category="transport",
        extractor=None,
    ),
    FieldDefinition(
        name="vehicle_dimensions",
        label="Габариты машины",
        type="array",
        required_by_default=False,
        category="transport",
        extractor=_extract_vehicle_dimensions,
        validator=_is_valid_dimensions,
    ),
    FieldDefinition(
        name="temperature_range",
        label="Температурный режим",
        type="string",
        required_by_default=False,
        category="transport",
        extractor=None,
    ),
    # ... можно добавить остальные поля по мере необходимости
]
