"""World-level helpers: model streaming, entity spawning."""

from __future__ import annotations

from typing import Union

import gta
from gta.natives.misc import GET_HASH_KEY
from gta.natives.ped import CREATE_PED
from gta.natives.streaming import (
    HAS_MODEL_LOADED,
    IS_MODEL_VALID,
    REQUEST_MODEL,
    SET_MODEL_AS_NO_LONGER_NEEDED,
)
from gta.natives.vehicle import CREATE_VEHICLE
from gta.ped import Ped
from gta.vehicle import Vehicle


Model = Union[str, int]
Position = Union["gta.Vector3", tuple[float, float, float]]


def _resolve_model(m: Model) -> int:
    return GET_HASH_KEY(m) if isinstance(m, str) else m


def load_model(model: Model, timeout_ms: int = 2500) -> int:
    """Request a model from streaming and wait until loaded.

    Returns the model hash on success, 0 on failure (invalid model or timeout).
    Call `release_model(hash)` once your entities are spawned.
    """
    h = _resolve_model(model)
    if not IS_MODEL_VALID(h):
        gta.log("warn", f"model {model!r} is not valid")
        return 0
    REQUEST_MODEL(h)
    ticks = max(1, timeout_ms // 50)
    for _ in range(ticks):
        if HAS_MODEL_LOADED(h):
            return h
        gta.wait(50)
    gta.log("warn", f"model {model!r} failed to stream within {timeout_ms}ms")
    return 0


def release_model(model: Model) -> None:
    SET_MODEL_AS_NO_LONGER_NEEDED(_resolve_model(model))


def create_ped(model: Model, pos: Position, heading: float = 0.0,
               ped_type: int = 26, release_model_after: bool = True) -> Ped | None:
    """Spawn a ped. ped_type: 4=civ_male, 26=civ_mil, 29=military, 30=cop."""
    h = load_model(model)
    if not h:
        return None
    x, y, z = pos
    handle = CREATE_PED(ped_type, h, x, y, z, heading, False, False)
    if release_model_after:
        release_model(h)
    return Ped(handle) if handle else None


def create_vehicle(model: Model, pos: Position, heading: float = 0.0,
                   release_model_after: bool = True) -> Vehicle | None:
    h = load_model(model)
    if not h:
        return None
    x, y, z = pos
    handle = CREATE_VEHICLE(h, x, y, z, heading, False, False, False)
    if release_model_after:
        release_model(h)
    return Vehicle(handle) if handle else None


def get_all_peds() -> list[Ped]:
    return [Ped(h) for h in gta.get_all_peds()]


def get_all_vehicles() -> list[Vehicle]:
    return [Vehicle(h) for h in gta.get_all_vehicles()]
