"""Tests for TorrServer API client response normalization.

Regression tests for issue #193: TorrServer may return a JSON list
instead of a dict for some endpoints, causing TypeError when
accessing ["hash"] on the list.
"""

from unittest.mock import MagicMock

import pytest

from lib.api.jacktorr.jacktorr import TorrServer, TorrServerError


def _make_response(json_data, status_code=200):
    """Create a mock requests.Response with the given json data and status code."""
    response = MagicMock()
    response.json.return_value = json_data
    response.status_code = status_code
    response.text = str(json_data)
    return response


@pytest.fixture
def torrserver():
    """Create a TorrServer instance with a mock session."""
    session = MagicMock()
    return TorrServer(
        host="localhost",
        port=8090,
        username="admin",
        password="pass",
        ssl_enabled=False,
        session=session,
    )


class TestNormalizeJsonResponse:
    """Tests for TorrServer._normalize_json_response."""

    def test_dict_passthrough(self):
        """Dict responses pass through unchanged."""
        data = {"hash": "abc123", "stat": 3}
        result = TorrServer._normalize_json_response(data)
        assert result == data

    def test_list_with_one_element_unwrapped(self):
        """A single-element list is unwrapped to its first element."""
        data = [{"hash": "abc123", "stat": 3}]
        result = TorrServer._normalize_json_response(data)
        assert result == {"hash": "abc123", "stat": 3}

    def test_list_with_multiple_elements_takes_first(self):
        """A multi-element list takes only the first element."""
        data = [{"hash": "abc123"}, {"hash": "def456"}]
        result = TorrServer._normalize_json_response(data)
        assert result == {"hash": "abc123"}

    def test_empty_list_raises_error(self):
        """An empty list raises TorrServerError."""
        with pytest.raises(TorrServerError, match="empty list"):
            TorrServer._normalize_json_response([])

    def test_non_dict_non_list_passthrough(self):
        """Non-dict, non-list values pass through (edge case)."""
        # Strings or numbers should come through as-is; the caller
        # will get a KeyError when accessing ["hash"], which is expected.
        result = TorrServer._normalize_json_response(42)
        assert result == 42


class TestExtractHash:
    """Tests for TorrServer._extract_hash."""

    def test_dict_response_returns_hash(self, torrserver):
        """Standard dict response extracts hash correctly."""
        response = _make_response({"hash": "abc123", "stat": 3})
        result = torrserver._extract_hash(response)
        assert result == "abc123"

    def test_list_response_extracts_hash(self, torrserver):
        """List-wrapped response is unwrapped and hash is extracted."""
        response = _make_response([{"hash": "abc123", "stat": 3}])
        result = torrserver._extract_hash(response)
        assert result == "abc123"

    def test_list_with_multiple_items_takes_first_hash(self, torrserver):
        """Multi-item list takes first element's hash."""
        response = _make_response([{"hash": "first_hash"}, {"hash": "second_hash"}])
        result = torrserver._extract_hash(response)
        assert result == "first_hash"

    def test_empty_list_raises_error(self, torrserver):
        """Empty list response raises TorrServerError."""
        response = _make_response([])
        with pytest.raises(TorrServerError, match="empty list"):
            torrserver._extract_hash(response)

    def test_missing_hash_key_raises_key_error(self, torrserver):
        """Response without 'hash' key raises KeyError."""
        response = _make_response({"stat": 3, "name": "test"})
        with pytest.raises(KeyError):
            torrserver._extract_hash(response)


class TestAddMagnet:
    """Tests for TorrServer.add_magnet with normalized responses."""

    def test_add_magnet_dict_response(self, torrserver):
        """add_magnet handles dict response from TorrServer."""
        torrserver._session.request.return_value = _make_response({"hash": "magnet_hash_1"})
        result = torrserver.add_magnet("magnet:?xt=urn:btih:abc123")
        assert result == "magnet_hash_1"

    def test_add_magnet_list_response(self, torrserver):
        """add_magnet handles list response from TorrServer (issue #193)."""
        torrserver._session.request.return_value = _make_response([{"hash": "magnet_hash_1"}])
        result = torrserver.add_magnet("magnet:?xt=urn:btih:abc123")
        assert result == "magnet_hash_1"


class TestAddTorrentObj:
    """Tests for TorrServer.add_torrent_obj with normalized responses."""

    def test_add_torrent_obj_dict_response(self, torrserver):
        """add_torrent_obj handles dict response from TorrServer."""
        torrserver._session.request.return_value = _make_response({"hash": "torrent_hash_1"})
        mock_file = MagicMock()
        result = torrserver.add_torrent_obj(mock_file)
        assert result == "torrent_hash_1"

    def test_add_torrent_obj_list_response(self, torrserver):
        """add_torrent_obj handles list response from TorrServer (issue #193)."""
        torrserver._session.request.return_value = _make_response([{"hash": "torrent_hash_1"}])
        mock_file = MagicMock()
        result = torrserver.add_torrent_obj(mock_file)
        assert result == "torrent_hash_1"


class TestAddTorrent:
    """Tests for TorrServer.add_torrent with normalized responses."""

    def test_add_torrent_dict_response(self, torrserver, tmp_path):
        """add_torrent handles dict response from TorrServer."""
        torrserver._session.request.return_value = _make_response({"hash": "file_hash_1"})
        torrent_file = tmp_path / "test.torrent"
        torrent_file.write_bytes(b"d8:announce17:http://test.com4:info6:teste")
        result = torrserver.add_torrent(str(torrent_file))
        assert result == "file_hash_1"

    def test_add_torrent_list_response(self, torrserver, tmp_path):
        """add_torrent handles list response from TorrServer (issue #193)."""
        torrserver._session.request.return_value = _make_response([{"hash": "file_hash_1"}])
        torrent_file = tmp_path / "test.torrent"
        torrent_file.write_bytes(b"d8:announce17:http://test.com4:info6:teste")
        result = torrserver.add_torrent(str(torrent_file))
        assert result == "file_hash_1"


class TestGetTorrentInfo:
    """Tests for TorrServer info methods with normalized responses."""

    def test_get_torrent_info_dict(self, torrserver):
        """get_torrent_info handles dict response."""
        expected = {"hash": "abc123", "stat": 3, "file_stats": []}
        torrserver._session.request.return_value = _make_response(expected)
        result = torrserver.get_torrent_info("abc123")
        assert result == expected

    def test_get_torrent_info_list(self, torrserver):
        """get_torrent_info handles list response (defensive)."""
        expected_inner = {"hash": "abc123", "stat": 3, "file_stats": []}
        torrserver._session.request.return_value = _make_response([expected_inner])
        result = torrserver.get_torrent_info("abc123")
        assert result == expected_inner

    def test_torrents_dict(self, torrserver):
        """torrents() handles dict response."""
        expected = {"torrents": []}
        torrserver._session.request.return_value = _make_response(expected)
        result = torrserver.torrents()
        assert result == expected

    def test_torrents_list(self, torrserver):
        """torrents() returns raw list from the list endpoint."""
        expected = [{"hash": "abc123"}]
        torrserver._session.request.return_value = _make_response(expected)
        result = torrserver.torrents()
        assert result == expected

    def test_torrents_empty_list(self, torrserver):
        """torrents() handles empty list gracefully (no TorrServerError)."""
        torrserver._session.request.return_value = _make_response([])
        result = torrserver.torrents()
        assert result == []
