import http.client
import json
import os
import pathlib
import tempfile
import sys
import threading
import unittest
from unittest import mock

sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1]))

from server import DEFAULT_HOST, DEFAULT_PORT, create_server, resolve_bind_address


class ServerTestCase(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.temp_dir = tempfile.TemporaryDirectory()
        cls.original_cwd = os.getcwd()
        os.chdir(cls.temp_dir.name)
        with open("index.html", "w", encoding="utf-8") as f:
            f.write("ok")
        cls.server = create_server("127.0.0.1", 0)
        cls.port = cls.server.server_address[1]
        cls.thread = threading.Thread(target=cls.server.serve_forever, daemon=True)
        cls.thread.start()

    @classmethod
    def tearDownClass(cls):
        cls.server.shutdown()
        cls.server.server_close()
        cls.thread.join(timeout=2)
        os.chdir(cls.original_cwd)
        cls.temp_dir.cleanup()

    def request(self, path: str, method: str = "GET", body: bytes | None = None, headers: dict | None = None):
        conn = http.client.HTTPConnection("127.0.0.1", self.port, timeout=2)
        conn.request(method, path, body=body, headers=headers or {})
        response = conn.getresponse()
        body = response.read()
        conn.close()
        return response, body

    def test_health_endpoint(self):
        response, body = self.request("/health")
        self.assertEqual(response.status, 200)
        self.assertEqual(response.getheader("Content-Type"), "application/json; charset=utf-8")
        self.assertEqual(json.loads(body.decode("utf-8")), {"status": "ok"})

    def test_not_found(self):
        response, _ = self.request("/missing")
        self.assertEqual(response.status, 404)

    def test_static_file_served(self):
        response, body = self.request("/index.html")
        self.assertEqual(response.status, 200)
        self.assertIn(b"ok", body)

    def test_default_bind_address(self):
        with mock.patch.dict(os.environ, {}, clear=True):
            host, port = resolve_bind_address()
        self.assertEqual(host, DEFAULT_HOST)
        self.assertEqual(port, DEFAULT_PORT)

    def test_submit_suggestion(self):
        payload = {
            "requester": "Casey",
            "title": "Add filters",
            "details": "Add status and owner filters to the portal.",
        }
        response, body = self.request(
            "/api/suggestions",
            method="POST",
            body=json.dumps(payload).encode("utf-8"),
            headers={"Content-Type": "application/json"},
        )
        self.assertEqual(response.status, 201)
        data = json.loads(body.decode("utf-8"))
        self.assertIn("suggestion", data)
        self.assertEqual(data["suggestion"]["requester"], payload["requester"])
        self.assertIn("id", data["suggestion"])
        self.assertIn("doneAt", data["suggestion"])

    def test_submit_suggestion_validation(self):
        payload = {"requester": "Casey", "title": "", "details": "Missing title"}
        response, body = self.request(
            "/api/suggestions",
            method="POST",
            body=json.dumps(payload).encode("utf-8"),
            headers={"Content-Type": "application/json"},
        )
        self.assertEqual(response.status, 400)
        data = json.loads(body.decode("utf-8"))
        self.assertIn("error", data)

    def test_list_suggestions(self):
        payload = {
            "requester": "Priya",
            "title": "Improve mobile spacing",
            "details": "Tighten paddings and reduce card width on phones.",
        }
        create_response, _ = self.request(
            "/api/suggestions",
            method="POST",
            body=json.dumps(payload).encode("utf-8"),
            headers={"Content-Type": "application/json"},
        )
        self.assertEqual(create_response.status, 201)

        response, body = self.request("/api/suggestions")
        self.assertEqual(response.status, 200)
        data = json.loads(body.decode("utf-8"))
        self.assertGreaterEqual(data["count"], 1)
        self.assertTrue(any(item.get("title") == payload["title"] for item in data["suggestions"]))

    def test_mark_suggestion_done_hides_from_active_queue(self):
        payload = {
            "requester": "Chris",
            "title": "Complete me",
            "details": "Mark me done.",
        }
        create_response, create_body = self.request(
            "/api/suggestions",
            method="POST",
            body=json.dumps(payload).encode("utf-8"),
            headers={"Content-Type": "application/json"},
        )
        self.assertEqual(create_response.status, 201)
        created = json.loads(create_body.decode("utf-8"))["suggestion"]

        done_response, _ = self.request(
            "/api/suggestions/mark-done",
            method="POST",
            body=json.dumps({"id": created["id"]}).encode("utf-8"),
            headers={"Content-Type": "application/json"},
        )
        self.assertEqual(done_response.status, 200)

        active_response, active_body = self.request("/api/suggestions")
        self.assertEqual(active_response.status, 200)
        active = json.loads(active_body.decode("utf-8"))["suggestions"]
        self.assertFalse(any(item.get("id") == created["id"] for item in active))

        all_response, all_body = self.request("/api/suggestions?includeDone=1")
        self.assertEqual(all_response.status, 200)
        all_items = json.loads(all_body.decode("utf-8"))["suggestions"]
        done_item = next(item for item in all_items if item.get("id") == created["id"])
        self.assertIsNotNone(done_item.get("doneAt"))


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