import unittest
from unittest.mock import AsyncMock, MagicMock, patch
from MicroPie import Server
import os
import uuid

class TestServer(unittest.TestCase):
    def setUp(self):
        self.server = Server()

    def test_parse_cookies(self):
        cookie_header = "session_id=abc123; theme=dark"
        cookies = self.server._parse_cookies(cookie_header)
        self.assertEqual(cookies, {"session_id": "abc123", "theme": "dark"})

    def test_parse_cookies_empty(self):
        cookies = self.server._parse_cookies("")
        self.assertEqual(cookies, {})

    def test_redirect(self):
        location = "/new-path"
        status, body = self.server.redirect(location)
        self.assertEqual(status, 302)
        self.assertIn(location, body)

    @patch("os.path.isfile", return_value=True)
    @patch("builtins.open", new_callable=MagicMock)
    def test_serve_static_file(self, mock_open, mock_isfile):
        mock_open.return_value.__enter__.return_value.read.return_value = b"file content"
        response = self.server.serve_static("test.txt")
        self.assertEqual(response[0], 200)
        self.assertEqual(response[1], b"file content")
        self.assertEqual(response[2][0][0], "Content-Type")

    @patch("os.path.isfile", return_value=False)
    def test_serve_static_file_not_found(self, mock_isfile):
        response = self.server.serve_static("missing.txt")
        self.assertEqual(response, (404, "404 Not Found"))

    def test_cleanup_sessions(self):
        self.server.sessions = {
            "session1": {"last_access": time.time() - 1000},
            "session2": {"last_access": time.time() - 10000},
        }
        self.server.SESSION_TIMEOUT = 3600
        self.server.cleanup_sessions()
        self.assertEqual(len(self.server.sessions), 1)
        self.assertIn("session1", self.server.sessions)

    @patch("uuid.uuid4", return_value="test-session-id")
    @patch("time.time", return_value=1000)
    async def test_asgi_app_creates_session(self, mock_time, mock_uuid):
        mock_send = AsyncMock()
        mock_receive = AsyncMock(return_value={"type": "http.request", "body": b""})

        scope = {
            "type": "http",
            "method": "GET",
            "path": "/",
            "headers": [],
        }

        async def mock_index():
            return "Hello, world!"

        self.server.index = mock_index

        await self.server.asgi_app(scope, mock_receive, mock_send)

        self.assertIn("test-session-id", self.server.sessions)
        self.assertEqual(self.server.sessions["test-session-id"].get("last_access"), 1000)

    @patch("time.time", return_value=1000)
    async def test_asgi_app_handles_request(self, mock_time):
        mock_send = AsyncMock()
        mock_receive = AsyncMock(return_value={"type": "http.request", "body": b""})

        scope = {
            "type": "http",
            "method": "GET",
            "path": "/",
            "headers": [(b"cookie", b"session_id=test-session-id")],
        }

        self.server.sessions["test-session-id"] = {"last_access": 500}

        async def mock_index():
            return "Hello, test!"

        self.server.index = mock_index

        await self.server.asgi_app(scope, mock_receive, mock_send)

        self.assertEqual(self.server.sessions["test-session-id"].get("last_access"), 1000)

    @patch("jinja2.Environment.get_template")
    def test_render_template(self, mock_get_template):
        mock_template = MagicMock()
        mock_template.render.return_value = "Rendered content"
        mock_get_template.return_value = mock_template

        result = self.server.render_template("test.html", var="value")
        self.assertEqual(result, "Rendered content")
        mock_template.render.assert_called_with({"var": "value"})

    def test_render_template_no_jinja(self):
        self.server.env = None
        with self.assertRaises(ImportError):
            self.server.render_template("test.html")

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