"""`acaua.device.resolve` — device string validation and priority ordering.

Priority: `cuda > mps > cpu`. Explicit device requests must be validated so
users get a clean `RuntimeError` rather than a cryptic torch trace when they
ask for a backend that isn't present.
"""

from __future__ import annotations

from unittest.mock import patch

import pytest
import torch

from acaua.device import resolve


def _cuda_yes(_self: object = None) -> bool:
    return True


def _cuda_no(_self: object = None) -> bool:
    return False


class TestAutoResolution:
    def test_auto_picks_cuda_when_available(self) -> None:
        with patch("torch.cuda.is_available", return_value=True):
            assert resolve("auto").type == "cuda"

    def test_auto_picks_mps_when_cuda_unavailable(self) -> None:
        with (
            patch("torch.cuda.is_available", return_value=False),
            patch("acaua.device._mps_available", return_value=True),
        ):
            assert resolve("auto").type == "mps"

    def test_auto_falls_back_to_cpu(self) -> None:
        with (
            patch("torch.cuda.is_available", return_value=False),
            patch("acaua.device._mps_available", return_value=False),
        ):
            assert resolve("auto").type == "cpu"


class TestExplicitDevice:
    def test_cpu_always_resolves(self) -> None:
        assert resolve("cpu") == torch.device("cpu")

    def test_cuda_passes_when_available(self) -> None:
        with patch("torch.cuda.is_available", return_value=True):
            assert resolve("cuda").type == "cuda"

    def test_cuda_raises_when_unavailable(self) -> None:
        with (
            patch("torch.cuda.is_available", return_value=False),
            pytest.raises(RuntimeError, match="CUDA is not available"),
        ):
            resolve("cuda")

    def test_cuda_with_index_passes_when_available(self) -> None:
        with patch("torch.cuda.is_available", return_value=True):
            result = resolve("cuda:1")
            assert result.type == "cuda"
            assert result.index == 1

    def test_mps_passes_when_available(self) -> None:
        with patch("acaua.device._mps_available", return_value=True):
            assert resolve("mps").type == "mps"

    def test_mps_raises_when_unavailable(self) -> None:
        with (
            patch("acaua.device._mps_available", return_value=False),
            pytest.raises(RuntimeError, match="MPS is not available"),
        ):
            resolve("mps")
