# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
"""Pytest marks and decorators for hardware / resource selection (CUDA, ROCm, …)."""

import pytest
from vllm.platforms import current_platform

# Re-exported from tests.helpers.env (GPU wait + DeviceMemoryMonitor).


def cuda_marks(*, res: str, num_cards: int):
    test_platform_detail = pytest.mark.cuda
    if res == "L4":
        test_resource = pytest.mark.L4
    elif res == "H100":
        test_resource = pytest.mark.H100
    else:
        raise ValueError(f"Invalid CUDA resource type: {res}. Supported: L4, H100")
    marks = [test_resource, test_platform_detail]
    if num_cards == 1:
        return marks
    test_distributed = pytest.mark.distributed_cuda(num_cards=num_cards)

    if not current_platform.is_cuda():
        return marks + [test_distributed]
    test_skipif = pytest.mark.skipif(
        current_platform.device_count() < num_cards,
        reason=f"Need at least {num_cards} CUDA GPUs to run the test.",
    )
    return marks + [test_distributed, test_skipif]


def rocm_marks(*, res: str, num_cards: int):
    test_platform_detail = pytest.mark.rocm
    if res == "MI325":
        test_resource = pytest.mark.MI325
    else:
        raise ValueError(f"Invalid ROCm resource type: {res}. Supported: MI325")
    marks = [test_resource, test_platform_detail]
    if num_cards == 1:
        return marks
    test_distributed = pytest.mark.distributed_rocm(num_cards=num_cards)
    return marks + [test_distributed]


def xpu_marks(*, res: str, num_cards: int):
    test_platform_detail = pytest.mark.xpu
    if res == "B60":
        test_resource = pytest.mark.B60
    else:
        raise ValueError(f"Invalid XPU resource type: {res}. Supported: B60")
    marks = [test_resource, test_platform_detail]
    if num_cards == 1:
        return marks
    test_distributed = pytest.mark.distributed_xpu(num_cards=num_cards)

    if not current_platform.is_xpu():
        return marks + [test_distributed]
    test_skipif = pytest.mark.skipif(
        current_platform.device_count() < num_cards,
        reason=f"Need at least {num_cards} XPUs to run the test.",
    )
    return marks + [test_distributed, test_skipif]


def musa_marks(*, res: str, num_cards: int):
    test_platform_detail = pytest.mark.musa
    if res == "S5000":
        test_resource = pytest.mark.S5000
    else:
        raise ValueError(f"Invalid MUSA resource type: {res}. Supported: S5000")
    marks = [test_resource, test_platform_detail]
    if num_cards == 1:
        return marks
    test_distributed = pytest.mark.distributed_musa(num_cards=num_cards)
    return marks + [test_distributed]


def gpu_marks(*, res: str, num_cards: int):
    test_platform = pytest.mark.gpu
    if res in ("L4", "H100"):
        return [test_platform] + cuda_marks(res=res, num_cards=num_cards)
    if res == "MI325":
        return [test_platform] + rocm_marks(res=res, num_cards=num_cards)
    if res == "B60":
        return [test_platform] + xpu_marks(res=res, num_cards=num_cards)
    if res == "S5000":
        return [test_platform] + musa_marks(res=res, num_cards=num_cards)
    raise ValueError(f"Invalid resource type: {res}. Supported: L4, H100, MI325, B60, S5000")


def npu_marks(*, res: str, num_cards: int):
    test_platform = pytest.mark.npu
    if res == "A2":
        test_resource = pytest.mark.A2
    elif res == "A3":
        test_resource = pytest.mark.A3
    else:
        test_resource = None
    if num_cards == 1:
        return [mark for mark in [test_platform, test_resource] if mark is not None]
    test_distributed = pytest.mark.distributed_npu(num_cards=num_cards)
    return [mark for mark in [test_platform, test_resource, test_distributed] if mark is not None]


def hardware_marks(*, res: dict[str, str], num_cards: int | dict[str, int] = 1):
    for platform, _ in res.items():
        if platform not in ("cuda", "rocm", "xpu", "npu", "musa"):
            raise ValueError(f"Unsupported platform: {platform}")
    if isinstance(num_cards, int):
        num_cards_dict = {platform: num_cards for platform in res.keys()}
    else:
        num_cards_dict = num_cards
        for platform in num_cards_dict.keys():
            if platform not in res:
                raise ValueError(f"Platform '{platform}' in num_cards but not in res.")
        for platform in res.keys():
            if platform not in num_cards_dict:
                num_cards_dict[platform] = 1

    all_marks: list[pytest.MarkDecorator] = []
    for platform, resource in res.items():
        cards = num_cards_dict[platform]
        if platform in ("cuda", "rocm", "xpu"):
            marks = gpu_marks(res=resource, num_cards=cards)
        elif platform == "musa":
            marks = musa_marks(res=resource, num_cards=cards)
        elif platform == "npu":
            marks = npu_marks(res=resource, num_cards=cards)
        else:
            raise ValueError(f"Unsupported platform: {platform}")
        all_marks.extend(marks)
    return all_marks


def hardware_test(*, res: dict[str, str], num_cards: int | dict[str, int] = 1):
    all_marks = hardware_marks(res=res, num_cards=num_cards)

    def wrapper(f):
        func = f
        for mark in reversed(all_marks):
            func = mark(func)
        return func

    return wrapper
