from __future__ import annotations

from pathlib import Path
from typing import Any

from liverylab_core.texture_descriptors import _inspect_material_texture_slots


NON_BODY_MARKERS = ("CARBON", "DESIGN", "PRESENTATION", "_PRES", "STRIPE")


def _resolve_content_path(game_root: Path, value: str) -> Path | None:
    raw = str(value or "").replace("\\", "/").strip()
    if not raw:
        return None
    relative = Path(*raw.split("/"))
    for candidate in (game_root / relative, game_root / Path(*relative.parts[1:]) if relative.parts and relative.parts[0].lower() == "content" else game_root / relative):
        if candidate.is_file():
            return candidate
    return None


def _base_material(path: Path, game_root: Path) -> Path:
    current = path
    seen: set[str] = set()
    for _index in range(8):
        key = str(current.resolve()).lower()
        if key in seen:
            break
        seen.add(key)
        inspected = _inspect_material_texture_slots(current)
        parent = _resolve_content_path(game_root, str(inspected.get("materialPath") or ""))
        if parent is None:
            break
        current = parent
    return current


def detect_material_surfaces(saved_car: dict[str, Any] | None, game_root: str | Path | None) -> list[dict[str, Any]]:
    if not isinstance(saved_car, dict) or not game_root:
        return []
    root = Path(game_root).expanduser()
    surfaces: list[dict[str, Any]] = []
    seen_batches: set[str] = set()
    for value in saved_car.get("materialRefs") or []:
        ref = str(value or "").strip()
        source_stem = Path(ref.replace("\\", "/")).stem.strip().upper()
        if not source_stem.startswith("EXT_SKIN"):
            continue
        source_path = _resolve_content_path(root, ref)
        if source_path is None:
            continue
        try:
            base_path = _base_material(source_path, root)
            source_inspection = _inspect_material_texture_slots(source_path)
        except Exception:
            continue
        batch_name = base_path.stem.strip().upper()
        if not batch_name.startswith("EXT_SKIN") or batch_name in seen_batches:
            continue
        seen_batches.add(batch_name)
        splat_ref = next((str(slot.get("value") or "").strip() for slot in source_inspection.get("textureSlots") or [] if str(slot.get("name") or "").strip().lower() == "spatmap"), "")
        splat_name = Path(splat_ref.replace("\\", "/")).name if splat_ref else ""
        splat_source = _resolve_content_path(root, splat_ref) if splat_ref else None
        surfaces.append({
            "id": batch_name.lower(),
            "batchName": batch_name,
            "sourceMaterial": ref,
            "baseMaterial": str(base_path),
            "sourceMaterialStem": source_stem,
            "splatTexture": f"{Path(splat_name).stem}.png" if splat_name else "",
            "splatTextureSource": str(splat_source) if splat_source else "",
            "bodyColorControlled": not any(marker in source_stem for marker in NON_BODY_MARKERS),
            "primary": not surfaces,
        })
    return surfaces


def body_color_batches(surfaces: list[dict[str, Any]] | None, fallback: str = "EXT_SKIN") -> list[str]:
    batches: list[str] = []
    for surface in surfaces or []:
        if not isinstance(surface, dict) or surface.get("bodyColorControlled") is not True:
            continue
        batch = str(surface.get("batchName") or "").strip()
        if batch and batch.upper() not in {row.upper() for row in batches}:
            batches.append(batch)
    return batches or [str(fallback or "").strip() or "EXT_SKIN"]
