# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project

import os
import pytest
import ray

from vllm.config.model import ModelDType
from vllm.sampling_params import SamplingParams
from vllm.v1.engine.async_llm import AsyncEngineArgs, AsyncLLM
from vllm.v1.metrics.ray_wrappers import RayPrometheusMetric, RayPrometheusStatLogger
from ...utils import models_path_prefix

MODELS = [
    os.path.join(models_path_prefix, "distilbert/distilgpt2"),
]


@pytest.mark.parametrize("model", MODELS)
@pytest.mark.parametrize("dtype", ["half"])
@pytest.mark.parametrize("max_tokens", [16])
def test_engine_log_metrics_ray(
    example_prompts,
    model: str,
    dtype: ModelDType,
    max_tokens: int,
) -> None:
    """Simple smoke test, verifying this can be used without exceptions.
    Need to start a Ray cluster in order to verify outputs."""

    @ray.remote(num_gpus=1)
    class EngineTestActor:
        async def run(self):
            engine_args = AsyncEngineArgs(
                model=model, dtype=dtype, disable_log_stats=False, enforce_eager=True
            )

            engine = AsyncLLM.from_engine_args(
                engine_args, stat_loggers=[RayPrometheusStatLogger]
            )

            for i, prompt in enumerate(example_prompts):
                results = engine.generate(
                    request_id=f"request-id-{i}",
                    prompt=prompt,
                    sampling_params=SamplingParams(max_tokens=max_tokens),
                )

                async for _ in results:
                    pass

    # Create the actor and call the async method
    actor = EngineTestActor.remote()  # type: ignore[attr-defined]
    ray.get(actor.run.remote())


def test_sanitized_opentelemetry_name():
    """Test the metric name sanitization logic for Ray."""

    # Only a-z, A-Z, 0-9, _, test valid characters are preserved
    valid_name = "valid_metric_123_abcDEF"
    assert (
        RayPrometheusMetric._get_sanitized_opentelemetry_name(valid_name) == valid_name
    )

    # Test dash, dot, are replaced
    name_with_dash_dot = "metric-name.test"
    expected = "metric_name_test"
    assert (
        RayPrometheusMetric._get_sanitized_opentelemetry_name(name_with_dash_dot)
        == expected
    )

    # Test colon is replaced with underscore
    name_with_colon = "metric:name"
    expected = "metric_name"
    assert (
        RayPrometheusMetric._get_sanitized_opentelemetry_name(name_with_colon)
        == expected
    )

    # Test multiple invalid characters are replaced
    name_with_invalid = "metric:name@with#special%chars"
    expected = "metric_name_with_special_chars"
    assert (
        RayPrometheusMetric._get_sanitized_opentelemetry_name(name_with_invalid)
        == expected
    )

    # Test mixed valid and invalid characters
    complex_name = "vllm:engine_stats/time.latency_ms-99p"
    expected = "vllm_engine_stats_time_latency_ms_99p"
    assert (
        RayPrometheusMetric._get_sanitized_opentelemetry_name(complex_name) == expected
    )

    # Test empty string
    assert RayPrometheusMetric._get_sanitized_opentelemetry_name("") == ""
