"""Tests for the Radix Edge CLI commands."""

from __future__ import annotations

from unittest.mock import MagicMock, patch

import httpx
from typer.testing import CliRunner

from radix_edge.cli.main import app

runner = CliRunner()


class TestSubmit:
    """Tests for the submit command."""

    def test_help(self):
        result = runner.invoke(app, ["submit", "--help"])
        assert result.exit_code == 0
        assert "Submit a new job" in result.output

    @patch("radix_edge.cli.main._post")
    def test_basic_submit(self, mock_post):
        mock_post.return_value = {"job_id": "job-abc123"}
        result = runner.invoke(app, ["submit", "ubuntu:22.04"])
        assert result.exit_code == 0
        assert "job-abc123" in result.output
        mock_post.assert_called_once()
        payload = mock_post.call_args[0][1]
        assert payload["image"] == "ubuntu:22.04"
        assert payload["num_gpus"] == 1
        assert payload["priority"] == 5

    @patch("radix_edge.cli.main._post")
    def test_submit_with_options(self, mock_post):
        mock_post.return_value = {"job_id": "job-xyz789"}
        result = runner.invoke(app, [
            "submit", "pytorch/pytorch:latest",
            "--gpus", "2",
            "--priority", "8",
            "--name", "training-run",
            "--user", "alice",
            "--timeout", "7200",
            "--type", "training",
        ])
        assert result.exit_code == 0
        payload = mock_post.call_args[0][1]
        assert payload["num_gpus"] == 2
        assert payload["priority"] == 8
        assert payload["name"] == "training-run"
        assert payload["user_id"] == "alice"
        assert payload["timeout_seconds"] == 7200
        assert payload["job_type"] == "training"

    @patch("radix_edge.cli.main._post")
    def test_submit_with_command(self, mock_post):
        mock_post.return_value = {"job_id": "job-cmd001"}
        result = runner.invoke(app, [
            "submit", "ubuntu:22.04",
            "--command", '["python", "train.py"]',
        ])
        assert result.exit_code == 0
        payload = mock_post.call_args[0][1]
        assert payload["command"] == ["python", "train.py"]

    @patch("radix_edge.cli.main._post", side_effect=httpx.ConnectError("Connection refused"))
    def test_submit_connection_error(self, mock_post):
        result = runner.invoke(app, ["submit", "ubuntu:22.04"])
        assert result.exit_code == 1
        assert "Error" in result.output


class TestStatus:
    """Tests for the status command."""

    def test_help(self):
        result = runner.invoke(app, ["status", "--help"])
        assert result.exit_code == 0
        assert "Get job status" in result.output

    @patch("radix_edge.cli.main._get")
    def test_status_success(self, mock_get):
        mock_get.return_value = {
            "status": "running",
            "image": "pytorch:latest",
            "user_id": "alice",
            "job_type": "training",
            "priority": 8,
            "num_gpus": 2,
            "scheduled_gpus": [0, 1],
            "created_at": "2026-03-04T10:00:00Z",
            "started_at": "2026-03-04T10:01:00Z",
            "completed_at": None,
            "runtime_seconds": None,
            "exit_code": None,
            "error_message": None,
        }
        result = runner.invoke(app, ["status", "job-abc123"])
        assert result.exit_code == 0
        assert "running" in result.output
        assert "pytorch" in result.output

    @patch("radix_edge.cli.main._get")
    def test_status_not_found(self, mock_get):
        resp = MagicMock()
        resp.status_code = 404
        resp.json.return_value = {"detail": "Job not found"}
        mock_get.side_effect = httpx.HTTPStatusError(
            "404", request=MagicMock(), response=resp
        )
        result = runner.invoke(app, ["status", "job-nonexistent"])
        assert result.exit_code == 1
        assert "not found" in result.output


class TestLs:
    """Tests for the ls command."""

    def test_help(self):
        result = runner.invoke(app, ["ls", "--help"])
        assert result.exit_code == 0

    @patch("radix_edge.cli.main._get")
    def test_ls_empty(self, mock_get):
        mock_get.return_value = {"jobs": []}
        result = runner.invoke(app, ["ls"])
        assert result.exit_code == 0
        assert "No jobs" in result.output

    @patch("radix_edge.cli.main._get")
    def test_ls_with_jobs(self, mock_get):
        mock_get.return_value = {
            "jobs": [
                {
                    "job_id": "job-001",
                    "status": "running",
                    "user_id": "alice",
                    "image": "pytorch:latest",
                    "num_gpus": 2,
                    "created_at": "2026-03-04T10:00:00Z",
                },
                {
                    "job_id": "job-002",
                    "status": "queued",
                    "user_id": "bob",
                    "image": "tensorflow:latest",
                    "num_gpus": 1,
                    "created_at": "2026-03-04T10:01:00Z",
                },
            ]
        }
        result = runner.invoke(app, ["ls"])
        assert result.exit_code == 0
        assert "job-001" in result.output
        assert "job-002" in result.output

    @patch("radix_edge.cli.main._get")
    def test_ls_with_filters(self, mock_get):
        mock_get.return_value = {"jobs": []}
        result = runner.invoke(app, ["ls", "--status", "running", "--user", "alice", "--limit", "5"])
        assert result.exit_code == 0
        call_path = mock_get.call_args[0][0]
        assert "status=running" in call_path
        assert "user_id=alice" in call_path
        assert "limit=5" in call_path


class TestCancel:
    """Tests for the cancel command."""

    def test_help(self):
        result = runner.invoke(app, ["cancel", "--help"])
        assert result.exit_code == 0

    @patch("radix_edge.cli.main._delete")
    def test_cancel_success(self, mock_delete):
        mock_delete.return_value = {"message": "Job cancelled"}
        result = runner.invoke(app, ["cancel", "job-abc123"])
        assert result.exit_code == 0
        assert "Cancelled" in result.output

    @patch("radix_edge.cli.main._delete")
    def test_cancel_not_found(self, mock_delete):
        resp = MagicMock()
        resp.status_code = 404
        resp.json.return_value = {"detail": "Job not found"}
        mock_delete.side_effect = httpx.HTTPStatusError(
            "404", request=MagicMock(), response=resp
        )
        result = runner.invoke(app, ["cancel", "job-nonexistent"])
        assert result.exit_code == 1
        assert "Error" in result.output


class TestGpus:
    """Tests for the gpus command."""

    def test_help(self):
        result = runner.invoke(app, ["gpus", "--help"])
        assert result.exit_code == 0

    @patch("radix_edge.cli.main._get")
    def test_gpus_empty(self, mock_get):
        mock_get.return_value = {"gpus": []}
        result = runner.invoke(app, ["gpus"])
        assert result.exit_code == 0
        assert "No GPUs" in result.output

    @patch("radix_edge.cli.main._get")
    def test_gpus_with_devices(self, mock_get):
        mock_get.return_value = {
            "gpus": [
                {
                    "gpu_index": 0,
                    "name": "NVIDIA RTX 4090",
                    "status": "idle",
                    "utilization_pct": 5.0,
                    "memory_used_mb": 512,
                    "memory_total_mb": 24576,
                    "temperature_c": 45.0,
                    "power_draw_w": 30.0,
                    "assigned_job_id": None,
                },
            ]
        }
        result = runner.invoke(app, ["gpus"])
        assert result.exit_code == 0
        assert "RTX 4090" in result.output
        assert "idle" in result.output


class TestConfig:
    """Tests for the config command."""

    def test_help(self):
        result = runner.invoke(app, ["config", "--help"])
        assert result.exit_code == 0

    @patch("radix_edge.cli.main._get")
    def test_config_show(self, mock_get):
        mock_get.return_value = {
            "tau": 0.8,
            "alpha": 0.1,
            "ema_beta": 0.15,
        }
        result = runner.invoke(app, ["config", "show"])
        assert result.exit_code == 0
        assert "tau" in result.output
        assert "0.8" in result.output

    @patch("radix_edge.cli.main._get")
    def test_config_connection_error(self, mock_get):
        mock_get.side_effect = httpx.ConnectError("Connection refused")
        result = runner.invoke(app, ["config", "show"])
        assert result.exit_code == 1
        assert "Error" in result.output
