"""Tests for the rollhub-dice SDK."""

import json
import unittest
from unittest.mock import MagicMock, patch

from rollhub_dice import (
    DiceAgent,
    Balance,
    BetResult,
    VerifyResult,
    AuthenticationError,
    InsufficientBalanceError,
    compute_roll,
    verify_server_seed,
    verify_bet,
)


class TestDiceAgent(unittest.TestCase):
    """Test the DiceAgent client against mocked responses."""

    def setUp(self):
        self.agent = DiceAgent(api_key="rh_sk_test", base_url="https://test.local/api/v1")

    def _mock_response(self, status_code=200, json_data=None):
        resp = MagicMock()
        resp.status_code = status_code
        resp.json.return_value = json_data or {}
        resp.text = json.dumps(json_data or {})
        return resp

    @patch("rollhub_dice.client.requests.Session.request")
    def test_balance(self, mock_req):
        mock_req.return_value = self._mock_response(200, {
            "balance_usd": "10.00",
            "balance_cents": 1000,
            "currency": "USD",
        })
        bal = self.agent.balance()
        self.assertIsInstance(bal, Balance)
        self.assertEqual(bal.balance_cents, 1000)
        self.assertEqual(str(bal), "10.00 USD")

    @patch("rollhub_dice.client.requests.Session.request")
    def test_bet(self, mock_req):
        mock_req.return_value = self._mock_response(200, {
            "bet_id": 1,
            "roll": 0.7234,
            "win": True,
            "payout": 198,
            "multiplier": 1.98,
            "balance": 1098,
            "proof": {
                "server_secret": "abc",
                "client_secret": "def",
                "nonce": 1,
                "server_seed_hash": "hash123",
            },
        })
        result = self.agent.bet(target=0.5, direction="over", amount=100)
        self.assertIsInstance(result, BetResult)
        self.assertTrue(result.win)
        self.assertEqual(result.payout, 198)
        self.assertEqual(result.proof.nonce, 1)

    @patch("rollhub_dice.client.requests.Session.request")
    def test_auth_error(self, mock_req):
        mock_req.return_value = self._mock_response(401, {"error": "invalid key"})
        with self.assertRaises(AuthenticationError):
            self.agent.balance()

    @patch("rollhub_dice.client.requests.Session.request")
    def test_insufficient_balance(self, mock_req):
        mock_req.return_value = self._mock_response(402, {"error": "insufficient balance"})
        with self.assertRaises(InsufficientBalanceError):
            self.agent.bet(target=0.5, amount=999999)

    @patch("rollhub_dice.client.requests.Session.request")
    def test_verify(self, mock_req):
        mock_req.return_value = self._mock_response(200, {
            "bet_id": 1,
            "verified": True,
            "roll": 0.7234,
        })
        v = self.agent.verify(bet_id=1)
        self.assertIsInstance(v, VerifyResult)
        self.assertTrue(v.verified)

    @patch("rollhub_dice.client.requests.Session.request")
    def test_retry_on_500(self, mock_req):
        fail = self._mock_response(500, {"error": "internal"})
        ok = self._mock_response(200, {"balance_usd": "5.00", "balance_cents": 500, "currency": "USD"})
        mock_req.side_effect = [fail, ok]
        bal = self.agent.balance()
        self.assertEqual(bal.balance_cents, 500)
        self.assertEqual(mock_req.call_count, 2)


class TestVerify(unittest.TestCase):
    """Test provably fair verification functions."""

    def test_verify_server_seed(self):
        import hashlib
        secret = "test_secret_123"
        expected = hashlib.sha3_384(secret.encode()).hexdigest()
        self.assertTrue(verify_server_seed(secret, expected))
        self.assertFalse(verify_server_seed(secret, "wrong"))

    def test_compute_roll_deterministic(self):
        r1 = compute_roll("server", "client", 1)
        r2 = compute_roll("server", "client", 1)
        self.assertEqual(r1, r2)
        self.assertGreaterEqual(r1, 0.0)
        self.assertLess(r1, 1.0)

    def test_compute_roll_different_nonce(self):
        r1 = compute_roll("server", "client", 1)
        r2 = compute_roll("server", "client", 2)
        self.assertNotEqual(r1, r2)

    def test_verify_bet_full(self):
        import hashlib
        secret = "my_server_secret"
        seed_hash = hashlib.sha3_384(secret.encode()).hexdigest()
        roll = compute_roll(secret, "my_client", 1)
        self.assertTrue(verify_bet(secret, "my_client", 1, seed_hash, roll))
        self.assertFalse(verify_bet(secret, "my_client", 1, seed_hash, 0.999))


if __name__ == "__main__":
    unittest.main()
