import http.client
import json
import threading
import unittest
from types import SimpleNamespace
from unittest.mock import Mock

from dataset import pilot_cases
from start_models import gateway


class ModelGatewayTests(unittest.TestCase):
    def setUp(self):
        self.models = SimpleNamespace(
            decision=SimpleNamespace(
                remote=Mock(return_value={"p_review": 0.73, "answer_mass": 0.98})
            ),
            sft=SimpleNamespace(
                remote=Mock(return_value={"risk_level": 4, "reasons": ["SHARED_BANK"]})
            ),
        )
        self.server = gateway(self.models, 0, token="test-token")
        self.thread = threading.Thread(target=self.server.serve_forever, daemon=True)
        self.thread.start()
        self.snapshot = next(pilot_cases())["input"]

    def tearDown(self):
        self.server.shutdown()
        self.server.server_close()
        self.thread.join()

    def request(self, path, body=None, token="test-token", method="POST"):
        connection = http.client.HTTPConnection(*self.server.server_address, timeout=5)
        try:
            headers = {
                "Authorization": "Bearer " + token,
                "Content-Type": "application/json",
            }
            connection.request(
                method, path, json.dumps(body) if body is not None else None, headers
            )
            response = connection.getresponse()
            return response.status, json.loads(response.read())
        finally:
            connection.close()

    def test_both_endpoints_return_the_elixir_contract_and_health_is_available(self):
        self.assertEqual(
            self.request("/decision", self.snapshot),
            (200, {"p_review": 0.73, "answer_mass": 0.98}),
        )
        self.assertEqual(
            self.request("/sft", self.snapshot),
            (200, {"risk_level": 4, "reasons": ["SHARED_BANK"]}),
        )
        status, health = self.request("/health", method="GET")
        self.assertEqual((status, health["status"]), (200, "ready"))
        self.assertEqual(self.server.server_address[0], "127.0.0.1")

    def test_rejects_bad_auth_input_and_outputs_without_returning_private_errors(self):
        self.assertEqual(
            self.request("/decision", self.snapshot, token="wrong")[0], 401
        )
        self.assertEqual(self.request("/missing", self.snapshot)[0], 404)
        self.assertEqual(self.request("/sft", {"case_id": "missing-fields"})[0], 400)
        self.models.decision.remote.return_value = {"p_review": True, "answer_mass": 1}
        self.assertEqual(
            self.request("/decision", self.snapshot),
            (503, {"error": "model_request_failed"}),
        )
        self.models.sft.remote.return_value = {"risk_level": 6, "reasons": ["UNKNOWN"]}
        self.assertEqual(self.request("/sft", self.snapshot)[0], 503)
        self.models.sft.remote.side_effect = RuntimeError(
            "private case text or credential"
        )
        self.assertEqual(
            self.request("/sft", self.snapshot),
            (503, {"error": "model_request_failed"}),
        )


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