"""
Tests for the Mizan SSR bridge and template backend.

Requires Bun installed and the test worker at packages/mizan-ssr/src/test-worker.tsx.
Tests skip gracefully if Bun is not available.
"""

import os
import shutil
import threading

from django.test import SimpleTestCase, RequestFactory

# Path to the test worker
_SSR_WORKER = os.path.join(
    os.path.dirname(__file__),
    "..", "..", "..", "..", "..",  # up to repo root
    "packages", "mizan-ssr", "src", "test-worker.tsx",
)
_SSR_WORKER = os.path.normpath(_SSR_WORKER)

_BUN_AVAILABLE = shutil.which("bun") is not None
_SKIP_MSG = "Bun not available"


class SSRBridgeTests(SimpleTestCase):
    """Tests for the SSR bridge subprocess manager."""

    def setUp(self):
        if not _BUN_AVAILABLE:
            self.skipTest(_SKIP_MSG)
        if not os.path.exists(_SSR_WORKER):
            self.skipTest(f"Test worker not found at {_SSR_WORKER}")

        from mizan.ssr.bridge import SSRBridge
        self.bridge = SSRBridge(worker_path=_SSR_WORKER, timeout=5.0)

    def tearDown(self):
        if hasattr(self, "bridge"):
            self.bridge.shutdown()

    def test_ping(self):
        """Worker starts and responds to ping."""
        self.assertTrue(self.bridge.ping())

    def test_render_simple(self):
        """Renders a simple component to HTML."""
        result = self.bridge.render("Hello", {"name": "World"})
        self.assertIn("Hello,", result.html)
        self.assertIn("World", result.html)

    def test_render_with_props(self):
        """Renders a component with multiple props."""
        result = self.bridge.render("UserProfile", {"user_id": 42, "name": "Alice"})
        self.assertIn("Alice", result.html)
        self.assertIn("42", result.html)

    def test_render_missing_component(self):
        """Rendering an unregistered component raises RuntimeError."""
        with self.assertRaises(RuntimeError) as ctx:
            self.bridge.render("NonExistent", {})
        self.assertIn("not registered", str(ctx.exception))

    def test_render_error(self):
        """Component that throws during render raises RuntimeError."""
        with self.assertRaises(RuntimeError) as ctx:
            self.bridge.render("Broken", {})
        self.assertIn("Render error", str(ctx.exception))

    def test_crash_recovery(self):
        """Bridge restarts the worker if it dies."""
        # First render works
        result = self.bridge.render("Hello", {"name": "Before"})
        self.assertIn("Before", result.html)

        # Kill the subprocess
        self.bridge._proc.kill()
        self.bridge._proc.wait()

        # Next render should restart and work
        result = self.bridge.render("Hello", {"name": "After"})
        self.assertIn("After", result.html)

    def test_concurrent_renders(self):
        """Multiple threads can render simultaneously."""
        results = {}
        errors = {}

        def render_in_thread(name: str, idx: int):
            try:
                result = self.bridge.render("Hello", {"name": name})
                results[idx] = result.html
            except Exception as e:
                errors[idx] = e

        threads = []
        for i in range(5):
            t = threading.Thread(target=render_in_thread, args=(f"User{i}", i))
            threads.append(t)
            t.start()

        for t in threads:
            t.join(timeout=10)

        self.assertEqual(len(errors), 0, f"Errors in concurrent renders: {errors}")
        self.assertEqual(len(results), 5)
        for i in range(5):
            self.assertIn(f"User{i}", results[i])


class SSRTemplateBackendTests(SimpleTestCase):
    """Tests for the MizanTemplates Django template backend."""

    def setUp(self):
        if not _BUN_AVAILABLE:
            self.skipTest(_SKIP_MSG)
        if not os.path.exists(_SSR_WORKER):
            self.skipTest(f"Test worker not found at {_SSR_WORKER}")

        from mizan.ssr.backend import MizanTemplates
        self.engine = MizanTemplates({
            "NAME": "mizan-test",
            "DIRS": [],
            "APP_DIRS": False,
            "OPTIONS": {
                "worker_path": _SSR_WORKER,
                "timeout": 5,
            },
        })
        self.factory = RequestFactory()

    def tearDown(self):
        if hasattr(self, "engine") and self.engine._bridge is not None:
            self.engine._bridge.shutdown()

    def test_get_template(self):
        """get_template returns a MizanTemplate."""
        from mizan.ssr.backend import MizanTemplate
        template = self.engine.get_template("Hello")
        self.assertIsInstance(template, MizanTemplate)
        self.assertEqual(template.component_name, "Hello")

    def test_template_render(self):
        """MizanTemplate.render() produces HTML."""
        template = self.engine.get_template("Hello")
        html = template.render({"name": "Django"})
        self.assertIn("Hello,", html)
        self.assertIn("Django", html)
        self.assertIn('data-mizan-component="Hello"', html)

    def test_template_render_strips_django_internals(self):
        """Django-internal context keys (request, csrf_token) are not passed as props."""
        template = self.engine.get_template("Hello")
        request = self.factory.get("/")
        html = template.render({"name": "Test", "request": request, "csrf_token": "abc"}, request)
        self.assertIn("Test", html)

    def test_from_string_raises(self):
        """from_string is not supported."""
        from django.template import TemplateDoesNotExist
        with self.assertRaises(TemplateDoesNotExist):
            self.engine.from_string("<div>Not supported</div>")
