import json
import os
import sys
import threading
import time
import unittest
from http.server import BaseHTTPRequestHandler, HTTPServer

sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))

from stele_sdk import Adapter, SteleClient


class AdapterRecordingHandler(BaseHTTPRequestHandler):
    last_query = None
    last_feedback = None
    last_write = None

    def log_message(self, format, *args):
        return

    def _read_json(self):
        length = int(self.headers.get("Content-Length", "0") or 0)
        if length == 0:
            return None
        raw = self.rfile.read(length)
        if not raw:
            return None
        return json.loads(raw.decode("utf-8"))

    def _send_json(self, status_code, payload):
        encoded = json.dumps(payload).encode("utf-8")
        self.send_response(status_code)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", str(len(encoded)))
        self.end_headers()
        self.wfile.write(encoded)

    def do_POST(self):
        payload = self._read_json()
        if self.path == "/memory.query":
            AdapterRecordingHandler.last_query = payload
            self._send_json(
                200,
                {
                    "meta": {"api_version": "v1", "request_id": "req-q", "timestamp_ms": 1},
                    "memories": [],
                    "abstain_reason": "",
                },
            )
            return
        if self.path == "/memory.feedback":
            AdapterRecordingHandler.last_feedback = payload
            self._send_json(200, {"meta": {"api_version": "v1", "request_id": "req-fb", "timestamp_ms": 1}})
            return
        if self.path == "/memory.write":
            AdapterRecordingHandler.last_write = payload
            self._send_json(
                200,
                {
                    "meta": {"api_version": "v1", "request_id": "req-w", "timestamp_ms": 1},
                    "memory": {"id": "mem-1"},
                },
            )
            return
        self.send_response(404)
        self.end_headers()


class StelePythonAdapterSmokeTest(unittest.TestCase):
    def test_adapter_query_and_record_turn(self):
        server = HTTPServer(("127.0.0.1", 0), AdapterRecordingHandler)
        thread = threading.Thread(target=server.serve_forever)
        thread.daemon = True
        thread.start()

        try:
            base_url = f"http://127.0.0.1:{server.server_port}"
            client = SteleClient(base_url=base_url, agent_id="agent-1", session_id="sess-1")
            adapter = Adapter(
                client=client,
                auto_write=True,
                auto_feedback=True,
                default_memory={"provenance": {"source_refs": ["run:sdk/python/tests/test_adapter_smoke.py"]}},
                budget_tokens=123,
            )

            with self.assertRaises(ValueError):
                adapter.query_memories("   ")

            adapter.query_memories("hello")
            self.assertIsNotNone(AdapterRecordingHandler.last_query)
            self.assertEqual(AdapterRecordingHandler.last_query.get("query_text"), "hello")
            self.assertEqual(AdapterRecordingHandler.last_query.get("budget_tokens"), 123)

            result = adapter.record_turn(
                {
                    "used_memory_ids": ["mem-used-1"],
                    "learnings": [
                        {
                            "memory": {
                                "content": "learning-1",
                                "scope": "SCOPE_AGENT",
                            },
                            "allow_update": True,
                        }
                    ],
                }
            )
            self.assertEqual(len(result.get("feedback") or []), 1)
            self.assertEqual(len(result.get("writes") or []), 1)

            self.assertIsNotNone(AdapterRecordingHandler.last_feedback)
            signal = (AdapterRecordingHandler.last_feedback or {}).get("signal") or {}
            self.assertEqual(signal.get("memory_id"), "mem-used-1")
            self.assertEqual(signal.get("agent_id"), "agent-1")
            self.assertEqual(signal.get("session_id"), "sess-1")

            self.assertIsNotNone(AdapterRecordingHandler.last_write)
            memory = (AdapterRecordingHandler.last_write or {}).get("memory") or {}
            self.assertEqual(memory.get("content"), "learning-1")
            self.assertEqual(memory.get("agent_id"), "agent-1")
            self.assertEqual(memory.get("session_id"), "sess-1")
            prov = memory.get("provenance") or {}
            self.assertTrue(isinstance(prov.get("source_refs"), list))
        finally:
            server.shutdown()
            server.server_close()
            thread.join(timeout=2)


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

