from __future__ import annotations

import json
import re
from pathlib import Path
from typing import Any


PREVIEW_ASSETS_DIRNAME = "PreviewAssets"
MANIFEST_FILENAME = "preview_assets.json"


def safe_car_id(value: Any, fallback: str = "car") -> str:
    raw = str(value or "").strip().replace("\\", "/")
    raw = raw.rstrip("/").split("/")[-1] if raw else ""
    raw = raw.lower()
    raw = re.sub(r"[^a-z0-9._-]+", "_", raw)
    raw = re.sub(r"_+", "_", raw).strip("._-")
    return raw or fallback


def root(livery_library_root: str | Path) -> Path:
    return Path(livery_library_root).expanduser() / PREVIEW_ASSETS_DIRNAME


def car_root(livery_library_root: str | Path, car_id: Any) -> Path:
    return root(livery_library_root) / safe_car_id(car_id)


def derived_car_root(livery_library_root: str | Path, car_id: Any) -> Path:
    return root(livery_library_root) / "Derived" / safe_car_id(car_id)


def _read_manifest(folder: Path) -> dict[str, Any]:
    path = folder / MANIFEST_FILENAME
    if not path.exists() or not path.is_file():
        return {}
    try:
        data = json.loads(path.read_text(encoding="utf-8-sig"))
    except Exception:
        return {}
    return data if isinstance(data, dict) else {}


def _first_existing(folder: Path, patterns: tuple[str, ...]) -> str:
    for pattern in patterns:
        try:
            match = next((path for path in folder.rglob(pattern) if path.is_file()), None)
        except Exception:
            match = None
        if match:
            try:
                return str(match.relative_to(folder))
            except Exception:
                return str(match)
    return ""


def _status_for_folder(folder: Path, clean_car_id: str, *, layout: str) -> dict[str, Any]:
    manifest_path = folder / MANIFEST_FILENAME
    manifest = _read_manifest(folder)
    signals = {
        "manifest": manifest_path.exists() and manifest_path.is_file(),
        "exteriorMesh": bool(_first_existing(folder, ("*exterior.mesh", "*exterior.glb", "exterior.glb"))),
        "uvTemplate": bool(_first_existing(folder, ("*UV*.png", "*uv*.png"))),
        "materialMetadata": bool(_first_existing(folder, ("*material*.json", "*materials*.json", "*.material"))),
    }
    manifest_ready = manifest.get("ready") is True
    ready = bool(folder.exists() and folder.is_dir() and (manifest_ready or (signals["exteriorMesh"] and signals["uvTemplate"])))
    missing: list[str] = []
    if not folder.exists():
        missing.append("preview asset folder")
    if not signals["exteriorMesh"]:
        missing.append("exterior mesh or GLB")
    if not signals["uvTemplate"]:
        missing.append("UV template PNG")
    return {
        "ready": ready,
        "carId": clean_car_id,
        "root": str(folder),
        "layout": layout,
        "exists": folder.exists() and folder.is_dir(),
        "manifestPath": str(manifest_path),
        "manifestExists": signals["manifest"],
        "manifest": manifest,
        "signals": signals,
        "missing": missing,
    }


def status(livery_library_root: str | Path | None, car_id: Any) -> dict[str, Any]:
    clean_car_id = safe_car_id(car_id)
    if not livery_library_root:
        return {
            "ready": False,
            "carId": clean_car_id,
            "root": "",
            "exists": False,
            "manifestExists": False,
            "signals": {},
            "missing": ["preview assets root unavailable"],
        }
    candidates = [
        _status_for_folder(car_root(livery_library_root, clean_car_id), clean_car_id, layout="direct"),
        _status_for_folder(derived_car_root(livery_library_root, clean_car_id), clean_car_id, layout="derived"),
    ]
    selected = next((item for item in candidates if item.get("ready")), None)
    if selected is None:
        selected = next((item for item in candidates if item.get("exists")), candidates[0])
    return {
        **selected,
        "candidates": candidates,
    }
